Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/*
3
 *  $Id: Connection.php 7490 2010-03-29 19:53:27Z jwage $
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.doctrine-project.org>.
20
 */
21
 
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.doctrine-project.org
51
 * @since       1.0
52
 * @version     $Revision: 7490 $
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, Serializable
57
{
58
    /**
59
     * @var $dbh                                the database handler
60
     */
61
    protected $dbh;
62
 
63
    /**
64
     * @var array $tables                       an array containing all the initialized Doctrine_Table objects
65
     *                                          keys representing Doctrine_Table component names and values as Doctrine_Table objects
66
     */
67
    protected $tables           = array();
68
 
69
    /**
70
     * $_name
71
     *
72
     * Name of the connection
73
     *
74
     * @var string $_name
75
     */
76
    protected $_name;
77
 
78
    /**
79
     * The name of this connection driver.
80
     *
81
     * @var string $driverName
82
     */
83
    protected $driverName;
84
 
85
    /**
86
     * @var boolean $isConnected                whether or not a connection has been established
87
     */
88
    protected $isConnected      = false;
89
 
90
    /**
91
     * @var array $supported                    an array containing all features this driver supports,
92
     *                                          keys representing feature names and values as
93
     *                                          one of the following (true, false, 'emulated')
94
     */
95
    protected $supported        = array();
96
 
97
    /**
98
     * @var array $pendingAttributes            An array of pending attributes. When setting attributes
99
     *                                          no connection is needed. When connected all the pending
100
     *                                          attributes are passed to the underlying adapter (usually PDO) instance.
101
     */
102
    protected $pendingAttributes  = array();
103
 
104
    /**
105
     * @var array $modules                      an array containing all modules
106
     *              transaction                 Doctrine_Transaction driver, handles savepoint and transaction isolation abstraction
107
     *
108
     *              expression                  Doctrine_Expression_Driver, handles expression abstraction
109
     *
110
     *              dataDict                    Doctrine_DataDict driver, handles datatype abstraction
111
     *
112
     *              export                      Doctrine_Export driver, handles db structure modification abstraction (contains
113
     *                                          methods such as alterTable, createConstraint etc.)
114
     *              import                      Doctrine_Import driver, handles db schema reading
115
     *
116
     *              sequence                    Doctrine_Sequence driver, handles sequential id generation and retrieval
117
     *
118
     *              unitOfWork                  Doctrine_Connection_UnitOfWork handles many orm functionalities such as object
119
     *                                          deletion and saving
120
     *
121
     *              formatter                   Doctrine_Formatter handles data formatting, quoting and escaping
122
     *
123
     * @see Doctrine_Connection::__get()
124
     * @see Doctrine_DataDict
125
     * @see Doctrine_Expression_Driver
126
     * @see Doctrine_Export
127
     * @see Doctrine_Transaction
128
     * @see Doctrine_Sequence
129
     * @see Doctrine_Connection_UnitOfWork
130
     * @see Doctrine_Formatter
131
     */
132
    private $modules = array('transaction' => false,
133
                             'expression'  => false,
134
                             'dataDict'    => false,
135
                             'export'      => false,
136
                             'import'      => false,
137
                             'sequence'    => false,
138
                             'unitOfWork'  => false,
139
                             'formatter'   => false,
140
                             'util'        => false,
141
                             );
142
 
143
    /**
144
     * @var array $properties               an array of connection properties
145
     */
146
    protected $properties = array('sql_comments'        => array(array('start' => '--', 'end' => "\n", 'escape' => false),
147
                                                                 array('start' => '/*', 'end' => '*/', 'escape' => false)),
148
                                  'identifier_quoting'  => array('start' => '"', 'end' => '"','escape' => '"'),
149
                                  'string_quoting'      => array('start' => "'",
150
                                                                 'end' => "'",
151
                                                                 'escape' => false,
152
                                                                 'escape_pattern' => false),
153
                                  'wildcards'           => array('%', '_'),
154
                                  'varchar_max_length'  => 255,
155
                                  'sql_file_delimiter'  => ";\n",
156
                                  'max_identifier_length' => 64,
157
                                  );
158
 
159
    /**
160
     * @var array $serverInfo
161
     */
162
    protected $serverInfo = array();
163
 
164
    protected $options    = array();
165
 
166
    /**
167
     * @var array $supportedDrivers         an array containing all supported drivers
168
     */
169
    private static $supportedDrivers    = array(
170
                                        'Mysql',
171
                                        'Pgsql',
172
                                        'Oracle',
173
                                        'Mssql',
174
                                        'Sqlite',
175
                                        );
176
    protected $_count = 0;
177
 
178
    /**
179
     * @var array $_userFkNames                 array of foreign key names that have been used
180
     */
181
    protected $_usedNames = array(
182
            'foreign_keys' => array(),
183
            'indexes' => array()
184
        );
185
 
186
    /**
187
     * the constructor
188
     *
189
     * @param Doctrine_Manager $manager                 the manager object
190
     * @param PDO|Doctrine_Adapter_Interface $adapter   database driver
191
     */
192
    public function __construct(Doctrine_Manager $manager, $adapter, $user = null, $pass = null)
193
    {
194
        if (is_object($adapter)) {
195
            if ( ! ($adapter instanceof PDO) && ! in_array('Doctrine_Adapter_Interface', class_implements($adapter))) {
196
                throw new Doctrine_Connection_Exception('First argument should be an instance of PDO or implement Doctrine_Adapter_Interface');
197
            }
198
            $this->dbh = $adapter;
199
 
200
            $this->isConnected = true;
201
 
202
        } else if (is_array($adapter)) {
203
            $this->pendingAttributes[Doctrine_Core::ATTR_DRIVER_NAME] = $adapter['scheme'];
204
 
205
            $this->options['dsn']      = $adapter['dsn'];
206
            $this->options['username'] = $adapter['user'];
207
            $this->options['password'] = $adapter['pass'];
208
 
209
            $this->options['other'] = array();
210
            if (isset($adapter['other'])) {
211
                $this->options['other'] = array(Doctrine_Core::ATTR_PERSISTENT => $adapter['persistent']);
212
            }
213
 
214
        }
215
 
216
        $this->setParent($manager);
217
 
218
        $this->setAttribute(Doctrine_Core::ATTR_CASE, Doctrine_Core::CASE_NATURAL);
219
        $this->setAttribute(Doctrine_Core::ATTR_ERRMODE, Doctrine_Core::ERRMODE_EXCEPTION);
220
 
221
        $this->getAttribute(Doctrine_Core::ATTR_LISTENER)->onOpen($this);
222
    }
223
 
224
    /**
225
     * Check wherther the connection to the database has been made yet
226
     *
227
     * @return boolean
228
     */
229
    public function isConnected()
230
    {
231
        return $this->isConnected;
232
    }
233
 
234
    /**
235
     * getOptions
236
     *
237
     * Get array of all options
238
     *
239
     * @return void
240
     */
241
    public function getOptions()
242
    {
243
      return $this->options;
244
    }
245
 
246
    /**
247
     * getOption
248
     *
249
     * Retrieves option
250
     *
251
     * @param string $option
252
     * @return void
253
     */
254
    public function getOption($option)
255
    {
256
        if (isset($this->options[$option])) {
257
            return $this->options[$option];
258
        }
259
    }
260
 
261
    /**
262
     * setOption
263
     *
264
     * Set option value
265
     *
266
     * @param string $option
267
     * @return void
268
     */
269
    public function setOption($option, $value)
270
    {
271
      return $this->options[$option] = $value;
272
    }
273
 
274
    /**
275
     * getAttribute
276
     * retrieves a database connection attribute
277
     *
278
     * @param integer $attribute
279
     * @return mixed
280
     */
281
    public function getAttribute($attribute)
282
    {
283
        if ($attribute >= 100 && $attribute < 1000) {
284
            if ( ! isset($this->attributes[$attribute])) {
285
                return parent::getAttribute($attribute);
286
            }
287
            return $this->attributes[$attribute];
288
        }
289
 
290
        if ($this->isConnected) {
291
            try {
292
                return $this->dbh->getAttribute($attribute);
293
            } catch (Exception $e) {
294
                throw new Doctrine_Connection_Exception('Attribute ' . $attribute . ' not found.');
295
            }
296
        } else {
297
            if ( ! isset($this->pendingAttributes[$attribute])) {
298
                $this->connect();
299
                $this->getAttribute($attribute);
300
            }
301
 
302
            return $this->pendingAttributes[$attribute];
303
        }
304
    }
305
 
306
    /**
307
     * returns an array of available PDO drivers
308
     */
309
    public static function getAvailableDrivers()
310
    {
311
        return PDO::getAvailableDrivers();
312
    }
313
 
314
    /**
315
     * Returns an array of supported drivers by Doctrine
316
     *
317
     * @return array $supportedDrivers
318
     */
319
    public static function getSupportedDrivers()
320
    {
321
        return self::$supportedDrivers;
322
    }
323
 
324
    /**
325
     * setAttribute
326
     * sets an attribute
327
     *
328
     * @todo why check for >= 100? has this any special meaning when creating
329
     * attributes?
330
     *
331
     * @param integer $attribute
332
     * @param mixed $value
333
     * @return boolean
334
     */
335
    public function setAttribute($attribute, $value)
336
    {
337
        if ($attribute >= 100 && $attribute < 1000) {
338
            parent::setAttribute($attribute, $value);
339
        } else {
340
            if ($this->isConnected) {
341
                $this->dbh->setAttribute($attribute, $value);
342
            } else {
343
                $this->pendingAttributes[$attribute] = $value;
344
            }
345
        }
346
 
347
        return $this;
348
    }
349
 
350
    /**
351
     * getName
352
     * returns the name of this driver
353
     *
354
     * @return string           the name of this driver
355
     */
356
    public function getName()
357
    {
358
        return $this->_name;
359
    }
360
 
361
    /**
362
     * setName
363
     *
364
     * Sets the name of the connection
365
     *
366
     * @param string $name
367
     * @return void
368
     */
369
    public function setName($name)
370
    {
371
        $this->_name = $name;
372
    }
373
 
374
    /**
375
     * getDriverName
376
     *
377
     * Gets the name of the instance driver
378
     *
379
     * @return void
380
     */
381
    public function getDriverName()
382
    {
383
        return $this->driverName;
384
    }
385
 
386
    /**
387
     * __get
388
     * lazy loads given module and returns it
389
     *
390
     * @see Doctrine_DataDict
391
     * @see Doctrine_Expression_Driver
392
     * @see Doctrine_Export
393
     * @see Doctrine_Transaction
394
     * @see Doctrine_Connection::$modules       all availible modules
395
     * @param string $name                      the name of the module to get
396
     * @throws Doctrine_Connection_Exception    if trying to get an unknown module
397
     * @return Doctrine_Connection_Module       connection module
398
     */
399
    public function __get($name)
400
    {
401
        if (isset($this->properties[$name])) {
402
            return $this->properties[$name];
403
        }
404
 
405
        if ( ! isset($this->modules[$name])) {
406
            throw new Doctrine_Connection_Exception('Unknown module / property ' . $name);
407
        }
408
        if ($this->modules[$name] === false) {
409
            switch ($name) {
410
                case 'unitOfWork':
411
                    $this->modules[$name] = new Doctrine_Connection_UnitOfWork($this);
412
                    break;
413
                case 'formatter':
414
                    $this->modules[$name] = new Doctrine_Formatter($this);
415
                    break;
416
                default:
417
                    $class = 'Doctrine_' . ucwords($name) . '_' . $this->getDriverName();
418
                    $this->modules[$name] = new $class($this);
419
                }
420
        }
421
 
422
        return $this->modules[$name];
423
    }
424
 
425
    /**
426
     * returns the manager that created this connection
427
     *
428
     * @return Doctrine_Manager
429
     */
430
    public function getManager()
431
    {
432
        return $this->getParent();
433
    }
434
 
435
    /**
436
     * returns the database handler of which this connection uses
437
     *
438
     * @return PDO              the database handler
439
     */
440
    public function getDbh()
441
    {
442
        $this->connect();
443
 
444
        return $this->dbh;
445
    }
446
 
447
    /**
448
     * connect
449
     * connects into database
450
     *
451
     * @return boolean
452
     */
453
    public function connect()
454
    {
455
        if ($this->isConnected) {
456
            return false;
457
        }
458
 
459
        $event = new Doctrine_Event($this, Doctrine_Event::CONN_CONNECT);
460
 
461
        $this->getListener()->preConnect($event);
462
 
463
        $e     = explode(':', $this->options['dsn']);
464
        $found = false;
465
 
466
        if (extension_loaded('pdo')) {
467
            if (in_array($e[0], self::getAvailableDrivers())) {
468
                try {
469
                    $this->dbh = new PDO($this->options['dsn'], $this->options['username'],
470
                                     (!$this->options['password'] ? '':$this->options['password']), $this->options['other']);
471
 
472
                    $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
473
                } catch (PDOException $e) {
474
                    throw new Doctrine_Connection_Exception('PDO Connection Error: ' . $e->getMessage());
475
                }
476
                $found = true;
477
            }
478
        }
479
 
480
        if ( ! $found) {
481
            $class = 'Doctrine_Adapter_' . ucwords($e[0]);
482
 
483
            if (class_exists($class)) {
484
                $this->dbh = new $class($this->options['dsn'], $this->options['username'], $this->options['password'], $this->options);
485
            } else {
486
                throw new Doctrine_Connection_Exception("Couldn't locate driver named " . $e[0]);
487
            }
488
        }
489
 
490
        // attach the pending attributes to adapter
491
        foreach($this->pendingAttributes as $attr => $value) {
492
            // some drivers don't support setting this so we just skip it
493
            if ($attr == Doctrine_Core::ATTR_DRIVER_NAME) {
494
                continue;
495
            }
496
            $this->dbh->setAttribute($attr, $value);
497
        }
498
 
499
        $this->isConnected = true;
500
 
501
        $this->getListener()->postConnect($event);
502
        return true;
503
    }
504
 
505
    public function incrementQueryCount()
506
    {
507
        $this->_count++;
508
    }
509
 
510
    /**
511
     * converts given driver name
512
     *
513
     * @param
514
     */
515
    public function driverName($name)
516
    {
517
    }
518
 
519
    /**
520
     * supports
521
     *
522
     * @param string $feature   the name of the feature
523
     * @return boolean          whether or not this drivers supports given feature
524
     */
525
    public function supports($feature)
526
    {
527
        return (isset($this->supported[$feature])
528
                  && ($this->supported[$feature] === 'emulated'
529
                   || $this->supported[$feature]));
530
    }
531
 
532
    /**
533
     * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
534
     * query, except that if there is already a row in the table with the same
535
     * key field values, the REPLACE query just updates its values instead of
536
     * inserting a new row.
537
     *
538
     * The REPLACE type of query does not make part of the SQL standards. Since
539
     * practically only MySQL and SQLIte implement it natively, this type of
540
     * query isemulated through this method for other DBMS using standard types
541
     * of queries inside a transaction to assure the atomicity of the operation.
542
     *
543
     * @param                   string  name of the table on which the REPLACE query will
544
     *                          be executed.
545
     *
546
     * @param   array           an associative array that describes the fields and the
547
     *                          values that will be inserted or updated in the specified table. The
548
     *                          indexes of the array are the names of all the fields of the table.
549
     *
550
     *                          The values of the array are values to be assigned to the specified field.
551
     *
552
     * @param array $keys       an array containing all key fields (primary key fields
553
     *                          or unique index fields) for this table
554
     *
555
     *                          the uniqueness of a row will be determined according to
556
     *                          the provided key fields
557
     *
558
     *                          this method will fail if no key fields are specified
559
     *
560
     * @throws Doctrine_Connection_Exception        if this driver doesn't support replace
561
     * @throws Doctrine_Connection_Exception        if some of the key values was null
562
     * @throws Doctrine_Connection_Exception        if there were no key fields
563
     * @throws PDOException                         if something fails at PDO level
564
     * @ return integer                              number of rows affected
565
     */
566
    public function replace(Doctrine_Table $table, array $fields, array $keys)
567
    {
568
        if (empty($keys)) {
569
            throw new Doctrine_Connection_Exception('Not specified which fields are keys');
570
        }
571
        $identifier = (array) $table->getIdentifier();
572
        $condition = array();
573
 
574
        foreach ($fields as $fieldName => $value) {
575
            if (in_array($fieldName, $keys)) {
576
                if ($value !== null) {
577
                    $condition[] = $table->getColumnName($fieldName) . ' = ?';
578
                    $conditionValues[] = $value;
579
                }
580
            }
581
        }
582
 
583
        $affectedRows = 0;
584
        if ( ! empty($condition) && ! empty($conditionValues)) {
585
            $query = 'DELETE FROM ' . $this->quoteIdentifier($table->getTableName())
586
                    . ' WHERE ' . implode(' AND ', $condition);
587
 
588
            $affectedRows = $this->exec($query, $conditionValues);
589
        }
590
 
591
        $this->insert($table, $fields);
592
 
593
        $affectedRows++;
594
 
595
        return $affectedRows;
596
    }
597
 
598
    /**
599
     * deletes table row(s) matching the specified identifier
600
     *
601
     * @throws Doctrine_Connection_Exception    if something went wrong at the database level
602
     * @param string $table         The table to delete data from
603
     * @param array $identifier     An associateve array containing identifier column-value pairs.
604
     * @return integer              The number of affected rows
605
     */
606
    public function delete(Doctrine_Table $table, array $identifier)
607
    {
608
        $tmp = array();
609
 
610
        foreach (array_keys($identifier) as $id) {
611
            $tmp[] = $this->quoteIdentifier($table->getColumnName($id)) . ' = ?';
612
        }
613
 
614
        $query = 'DELETE FROM '
615
               . $this->quoteIdentifier($table->getTableName())
616
               . ' WHERE ' . implode(' AND ', $tmp);
617
 
618
        return $this->exec($query, array_values($identifier));
619
    }
620
 
621
    /**
622
     * Updates table row(s) with specified data.
623
     *
624
     * @throws Doctrine_Connection_Exception    if something went wrong at the database level
625
     * @param Doctrine_Table $table     The table to insert data into
626
     * @param array $values             An associative array containing column-value pairs.
627
     *                                  Values can be strings or Doctrine_Expression instances.
628
     * @return integer                  the number of affected rows. Boolean false if empty value array was given,
629
     */
630
    public function update(Doctrine_Table $table, array $fields, array $identifier)
631
    {
632
        if (empty($fields)) {
633
            return false;
634
        }
635
 
636
        $set = array();
637
        foreach ($fields as $fieldName => $value) {
638
            if ($value instanceof Doctrine_Expression) {
639
                $set[] = $this->quoteIdentifier($table->getColumnName($fieldName)) . ' = ' . $value->getSql();
640
                unset($fields[$fieldName]);
641
            } else {
642
                $set[] = $this->quoteIdentifier($table->getColumnName($fieldName)) . ' = ?';
643
            }
644
        }
645
 
646
        $params = array_merge(array_values($fields), array_values($identifier));
647
 
648
        $sql  = 'UPDATE ' . $this->quoteIdentifier($table->getTableName())
649
              . ' SET ' . implode(', ', $set)
650
              . ' WHERE ' . implode(' = ? AND ', $this->quoteMultipleIdentifier($table->getIdentifierColumnNames()))
651
              . ' = ?';
652
 
653
        return $this->exec($sql, $params);
654
    }
655
 
656
    /**
657
     * Inserts a table row with specified data.
658
     *
659
     * @param Doctrine_Table $table     The table to insert data into.
660
     * @param array $values             An associative array containing column-value pairs.
661
     *                                  Values can be strings or Doctrine_Expression instances.
662
     * @return integer                  the number of affected rows. Boolean false if empty value array was given,
663
     */
664
    public function insert(Doctrine_Table $table, array $fields)
665
    {
666
        $tableName = $table->getTableName();
667
 
668
        // column names are specified as array keys
669
        $cols = array();
670
        // the query VALUES will contain either expresions (eg 'NOW()') or ?
671
        $a = array();
672
        foreach ($fields as $fieldName => $value) {
673
            $cols[] = $this->quoteIdentifier($table->getColumnName($fieldName));
674
            if ($value instanceof Doctrine_Expression) {
675
                $a[] = $value->getSql();
676
                unset($fields[$fieldName]);
677
            } else {
678
                $a[] = '?';
679
            }
680
        }
681
 
682
        // build the statement
683
        $query = 'INSERT INTO ' . $this->quoteIdentifier($tableName)
684
                . ' (' . implode(', ', $cols) . ')'
685
                . ' VALUES (' . implode(', ', $a) . ')';
686
 
687
        return $this->exec($query, array_values($fields));
688
    }
689
 
690
    /**
691
     * Quote a string so it can be safely used as a table or column name
692
     *
693
     * Delimiting style depends on which database driver is being used.
694
     *
695
     * NOTE: just because you CAN use delimited identifiers doesn't mean
696
     * you SHOULD use them.  In general, they end up causing way more
697
     * problems than they solve.
698
     *
699
     * Portability is broken by using the following characters inside
700
     * delimited identifiers:
701
     *   + backtick (<kbd>`</kbd>) -- due to MySQL
702
     *   + double quote (<kbd>"</kbd>) -- due to Oracle
703
     *   + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
704
     *
705
     * Delimited identifiers are known to generally work correctly under
706
     * the following drivers:
707
     *   + mssql
708
     *   + mysql
709
     *   + mysqli
710
     *   + oci8
711
     *   + pgsql
712
     *   + sqlite
713
     *
714
     * InterBase doesn't seem to be able to use delimited identifiers
715
     * via PHP 4.  They work fine under PHP 5.
716
     *
717
     * @param string $str           identifier name to be quoted
718
     * @param bool $checkOption     check the 'quote_identifier' option
719
     *
720
     * @return string               quoted identifier string
721
     */
722
    public function quoteIdentifier($str, $checkOption = true)
723
    {
724
        // quick fix for the identifiers that contain a dot
725
        if (strpos($str, '.')) {
726
            $e = explode('.', $str);
727
 
728
            return $this->formatter->quoteIdentifier($e[0], $checkOption) . '.'
729
                 . $this->formatter->quoteIdentifier($e[1], $checkOption);
730
        }
731
        return $this->formatter->quoteIdentifier($str, $checkOption);
732
    }
733
 
734
    /**
735
     * quoteMultipleIdentifier
736
     * Quotes multiple identifier strings
737
     *
738
     * @param array $arr           identifiers array to be quoted
739
     * @param bool $checkOption     check the 'quote_identifier' option
740
     *
741
     * @return string               quoted identifier string
742
     */
743
    public function quoteMultipleIdentifier($arr, $checkOption = true)
744
    {
745
        foreach ($arr as $k => $v) {
746
            $arr[$k] = $this->quoteIdentifier($v, $checkOption);
747
        }
748
 
749
        return $arr;
750
    }
751
 
752
    /**
753
     * convertBooleans
754
     * some drivers need the boolean values to be converted into integers
755
     * when using DQL API
756
     *
757
     * This method takes care of that conversion
758
     *
759
     * @param array $item
760
     * @return void
761
     */
762
    public function convertBooleans($item)
763
    {
764
        return $this->formatter->convertBooleans($item);
765
    }
766
 
767
    /**
768
     * quote
769
     * quotes given input parameter
770
     *
771
     * @param mixed $input      parameter to be quoted
772
     * @param string $type
773
     * @return string
774
     */
775
    public function quote($input, $type = null)
776
    {
777
        return $this->formatter->quote($input, $type);
778
    }
779
 
780
    /**
781
     * Set the date/time format for the current connection
782
     *
783
     * @param string    time format
784
     *
785
     * @return void
786
     */
787
    public function setDateFormat($format = null)
788
    {
789
    }
790
 
791
    /**
792
     * fetchAll
793
     *
794
     * @param string $statement         sql query to be executed
795
     * @param array $params             prepared statement params
796
     * @return array
797
     */
798
    public function fetchAll($statement, array $params = array())
799
    {
800
        return $this->execute($statement, $params)->fetchAll(Doctrine_Core::FETCH_ASSOC);
801
    }
802
 
803
    /**
804
     * fetchOne
805
     *
806
     * @param string $statement         sql query to be executed
807
     * @param array $params             prepared statement params
808
     * @param int $colnum               0-indexed column number to retrieve
809
     * @return mixed
810
     */
811
    public function fetchOne($statement, array $params = array(), $colnum = 0)
812
    {
813
        return $this->execute($statement, $params)->fetchColumn($colnum);
814
    }
815
 
816
    /**
817
     * fetchRow
818
     *
819
     * @param string $statement         sql query to be executed
820
     * @param array $params             prepared statement params
821
     * @return array
822
     */
823
    public function fetchRow($statement, array $params = array())
824
    {
825
        return $this->execute($statement, $params)->fetch(Doctrine_Core::FETCH_ASSOC);
826
    }
827
 
828
    /**
829
     * fetchArray
830
     *
831
     * @param string $statement         sql query to be executed
832
     * @param array $params             prepared statement params
833
     * @return array
834
     */
835
    public function fetchArray($statement, array $params = array())
836
    {
837
        return $this->execute($statement, $params)->fetch(Doctrine_Core::FETCH_NUM);
838
    }
839
 
840
    /**
841
     * fetchColumn
842
     *
843
     * @param string $statement         sql query to be executed
844
     * @param array $params             prepared statement params
845
     * @param int $colnum               0-indexed column number to retrieve
846
     * @return array
847
     */
848
    public function fetchColumn($statement, array $params = array(), $colnum = 0)
849
    {
850
        return $this->execute($statement, $params)->fetchAll(Doctrine_Core::FETCH_COLUMN, $colnum);
851
    }
852
 
853
    /**
854
     * fetchAssoc
855
     *
856
     * @param string $statement         sql query to be executed
857
     * @param array $params             prepared statement params
858
     * @return array
859
     */
860
    public function fetchAssoc($statement, array $params = array())
861
    {
862
        return $this->execute($statement, $params)->fetchAll(Doctrine_Core::FETCH_ASSOC);
863
    }
864
 
865
    /**
866
     * fetchBoth
867
     *
868
     * @param string $statement         sql query to be executed
869
     * @param array $params             prepared statement params
870
     * @return array
871
     */
872
    public function fetchBoth($statement, array $params = array())
873
    {
874
        return $this->execute($statement, $params)->fetchAll(Doctrine_Core::FETCH_BOTH);
875
    }
876
 
877
    /**
878
     * query
879
     * queries the database using Doctrine Query Language
880
     * returns a collection of Doctrine_Record objects
881
     *
882
     * <code>
883
     * $users = $conn->query('SELECT u.* FROM User u');
884
     *
885
     * $users = $conn->query('SELECT u.* FROM User u WHERE u.name LIKE ?', array('someone'));
886
     * </code>
887
     *
888
     * @param string $query             DQL query
889
     * @param array $params             query parameters
890
     * @param int $hydrationMode        Doctrine_Core::HYDRATE_ARRAY or Doctrine_Core::HYDRATE_RECORD
891
     * @see Doctrine_Query
892
     * @return Doctrine_Collection      Collection of Doctrine_Record objects
893
     */
894
    public function query($query, array $params = array(), $hydrationMode = null)
895
    {
896
        $parser = Doctrine_Query::create($this);
897
        $res = $parser->query($query, $params, $hydrationMode);
898
        $parser->free();
899
 
900
        return $res;
901
    }
902
 
903
    /**
904
     * prepare
905
     *
906
     * @param string $statement
907
     */
908
    public function prepare($statement)
909
    {
910
        $this->connect();
911
 
912
        try {
913
            $event = new Doctrine_Event($this, Doctrine_Event::CONN_PREPARE, $statement);
914
 
915
            $this->getAttribute(Doctrine_Core::ATTR_LISTENER)->prePrepare($event);
916
 
917
            $stmt = false;
918
 
919
            if ( ! $event->skipOperation) {
920
                $stmt = $this->dbh->prepare($statement);
921
            }
922
 
923
            $this->getAttribute(Doctrine_Core::ATTR_LISTENER)->postPrepare($event);
924
 
925
            return new Doctrine_Connection_Statement($this, $stmt);
926
        } catch(Doctrine_Adapter_Exception $e) {
927
        } catch(PDOException $e) { }
928
 
929
        $this->rethrowException($e, $this, $statement);
930
    }
931
 
932
    /**
933
     * query
934
     * queries the database using Doctrine Query Language and returns
935
     * the first record found
936
     *
937
     * <code>
938
     * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.id = ?', array(1));
939
     *
940
     * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.name LIKE ? AND u.password = ?',
941
     *         array('someone', 'password')
942
     *         );
943
     * </code>
944
     *
945
     * @param string $query             DQL query
946
     * @param array $params             query parameters
947
     * @see Doctrine_Query
948
     * @return Doctrine_Record|false    Doctrine_Record object on success,
949
     *                                  boolean false on failure
950
     */
951
    public function queryOne($query, array $params = array())
952
    {
953
        $parser = Doctrine_Query::create();
954
 
955
        $coll = $parser->query($query, $params);
956
        if ( ! $coll->contains(0)) {
957
            return false;
958
        }
959
        return $coll[0];
960
    }
961
 
962
    /**
963
     * queries the database with limit and offset
964
     * added to the query and returns a Doctrine_Connection_Statement object
965
     *
966
     * @param string $query
967
     * @param integer $limit
968
     * @param integer $offset
969
     * @return Doctrine_Connection_Statement
970
     */
971
    public function select($query, $limit = 0, $offset = 0)
972
    {
973
        if ($limit > 0 || $offset > 0) {
974
            $query = $this->modifyLimitQuery($query, $limit, $offset);
975
        }
976
        return $this->execute($query);
977
    }
978
 
979
    /**
980
     * standaloneQuery
981
     *
982
     * @param string $query     sql query
983
     * @param array $params     query parameters
984
     *
985
     * @return PDOStatement|Doctrine_Adapter_Statement
986
     */
987
    public function standaloneQuery($query, $params = array())
988
    {
989
        return $this->execute($query, $params);
990
    }
991
 
992
    /**
993
     * execute
994
     * @param string $query     sql query
995
     * @param array $params     query parameters
996
     *
997
     * @return PDOStatement|Doctrine_Adapter_Statement
998
     */
999
    public function execute($query, array $params = array())
1000
    {
1001
        $this->connect();
1002
 
1003
        try {
1004
            if ( ! empty($params)) {
1005
                $stmt = $this->prepare($query);
1006
                $stmt->execute($params);
1007
 
1008
                return $stmt;
1009
            } else {
1010
                $event = new Doctrine_Event($this, Doctrine_Event::CONN_QUERY, $query, $params);
1011
 
1012
                $this->getAttribute(Doctrine_Core::ATTR_LISTENER)->preQuery($event);
1013
 
1014
                if ( ! $event->skipOperation) {
1015
                    $stmt = $this->dbh->query($query);
1016
                    $this->_count++;
1017
                }
1018
                $this->getAttribute(Doctrine_Core::ATTR_LISTENER)->postQuery($event);
1019
 
1020
                return $stmt;
1021
            }
1022
        } catch (Doctrine_Adapter_Exception $e) {
1023
        } catch (PDOException $e) { }
1024
 
1025
        $this->rethrowException($e, $this, $query);
1026
    }
1027
 
1028
    /**
1029
     * exec
1030
     * @param string $query     sql query
1031
     * @param array $params     query parameters
1032
     *
1033
     * @return integer
1034
     */
1035
    public function exec($query, array $params = array())
1036
    {
1037
        $this->connect();
1038
 
1039
        try {
1040
            if ( ! empty($params)) {
1041
                $stmt = $this->prepare($query);
1042
                $stmt->execute($params);
1043
 
1044
                return $stmt->rowCount();
1045
            } else {
1046
                $event = new Doctrine_Event($this, Doctrine_Event::CONN_EXEC, $query, $params);
1047
 
1048
                $this->getAttribute(Doctrine_Core::ATTR_LISTENER)->preExec($event);
1049
                if ( ! $event->skipOperation) {
1050
                    $count = $this->dbh->exec($query);
1051
 
1052
                    $this->_count++;
1053
                }
1054
                $this->getAttribute(Doctrine_Core::ATTR_LISTENER)->postExec($event);
1055
 
1056
                return $count;
1057
            }
1058
        } catch (Doctrine_Adapter_Exception $e) {
1059
        } catch (PDOException $e) { }
1060
 
1061
        $this->rethrowException($e, $this, $query);
1062
    }
1063
 
1064
    /**
1065
     * rethrowException
1066
     *
1067
     * @throws Doctrine_Connection_Exception
1068
     */
1069
    public function rethrowException(Exception $e, $invoker, $query = null)
1070
    {
1071
        $event = new Doctrine_Event($this, Doctrine_Event::CONN_ERROR);
1072
 
1073
        $this->getListener()->preError($event);
1074
 
1075
        $name = 'Doctrine_Connection_' . $this->driverName . '_Exception';
1076
 
1077
        $message = $e->getMessage();
1078
        if ($query) {
1079
            $message .= sprintf('. Failing Query: "%s"', $query);
1080
        }
1081
 
1082
        $exc  = new $name($message, (int) $e->getCode());
1083
        if ( ! isset($e->errorInfo) || ! is_array($e->errorInfo)) {
1084
            $e->errorInfo = array(null, null, null, null);
1085
        }
1086
        $exc->processErrorInfo($e->errorInfo);
1087
 
1088
         if ($this->getAttribute(Doctrine_Core::ATTR_THROW_EXCEPTIONS)) {
1089
            throw $exc;
1090
        }
1091
 
1092
        $this->getListener()->postError($event);
1093
    }
1094
 
1095
    /**
1096
     * hasTable
1097
     * whether or not this connection has table $name initialized
1098
     *
1099
     * @param mixed $name
1100
     * @return boolean
1101
     */
1102
    public function hasTable($name)
1103
    {
1104
        return isset($this->tables[$name]);
1105
    }
1106
 
1107
    /**
1108
     * returns a table object for given component name
1109
     *
1110
     * @param string $name              component name
1111
     * @return Doctrine_Table
1112
     */
1113
    public function getTable($name)
1114
    {
1115
        if (isset($this->tables[$name])) {
1116
            return $this->tables[$name];
1117
        }
1118
 
1119
        $class = sprintf($this->getAttribute(Doctrine_Core::ATTR_TABLE_CLASS_FORMAT), $name);
1120
 
1121
        if (class_exists($class, $this->getAttribute(Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES)) &&
1122
                in_array('Doctrine_Table', class_parents($class))) {
1123
            $table = new $class($name, $this, true);
1124
        } else {
1125
            $tableClass = $this->getAttribute(Doctrine_Core::ATTR_TABLE_CLASS);
1126
            $table = new $tableClass($name, $this, true);
1127
        }
1128
 
1129
        return $table;
1130
    }
1131
 
1132
    /**
1133
     * returns an array of all initialized tables
1134
     *
1135
     * @return array
1136
     */
1137
    public function getTables()
1138
    {
1139
        return $this->tables;
1140
    }
1141
 
1142
    /**
1143
     * returns an iterator that iterators through all
1144
     * initialized table objects
1145
     *
1146
     * <code>
1147
     * foreach ($conn as $index => $table) {
1148
     *      print $table;  // get a string representation of each table object
1149
     * }
1150
     * </code>
1151
     *
1152
     * @return ArrayIterator        SPL ArrayIterator object
1153
     */
1154
    public function getIterator()
1155
    {
1156
        return new ArrayIterator($this->tables);
1157
    }
1158
 
1159
    /**
1160
     * returns the count of initialized table objects
1161
     *
1162
     * @return integer
1163
     */
1164
    public function count()
1165
    {
1166
        return $this->_count;
1167
    }
1168
 
1169
    /**
1170
     * addTable
1171
     * adds a Doctrine_Table object into connection registry
1172
     *
1173
     * @param $table                a Doctrine_Table object to be added into registry
1174
     * @return boolean
1175
     */
1176
    public function addTable(Doctrine_Table $table)
1177
    {
1178
        $name = $table->getComponentName();
1179
 
1180
        if (isset($this->tables[$name])) {
1181
            return false;
1182
        }
1183
        $this->tables[$name] = $table;
1184
        return true;
1185
    }
1186
 
1187
    /**
1188
     * create
1189
     * creates a record
1190
     *
1191
     * create                       creates a record
1192
     * @param string $name          component name
1193
     * @return Doctrine_Record      Doctrine_Record object
1194
     */
1195
    public function create($name)
1196
    {
1197
        return $this->getTable($name)->create();
1198
    }
1199
 
1200
    /**
1201
     * Creates a new Doctrine_Query object that operates on this connection.
1202
     *
1203
     * @return Doctrine_Query
1204
     */
1205
    public function createQuery()
1206
    {
1207
        return Doctrine_Query::create();
1208
    }
1209
 
1210
    /**
1211
     * flush
1212
     * saves all the records from all tables
1213
     * this operation is isolated using a transaction
1214
     *
1215
     * @throws PDOException         if something went wrong at database level
1216
     * @return void
1217
     */
1218
    public function flush()
1219
    {
1220
        try {
1221
            $this->beginInternalTransaction();
1222
            $this->unitOfWork->saveAll();
1223
            $this->commit();
1224
        } catch (Exception $e) {
1225
            $this->rollback();
1226
            throw $e;
1227
        }
1228
    }
1229
 
1230
    /**
1231
     * clear
1232
     * clears all repositories
1233
     *
1234
     * @return void
1235
     */
1236
    public function clear()
1237
    {
1238
        foreach ($this->tables as $k => $table) {
1239
            $table->getRepository()->evictAll();
1240
            $table->clear();
1241
        }
1242
    }
1243
 
1244
    /**
1245
     * evictTables
1246
     * evicts all tables
1247
     *
1248
     * @return void
1249
     */
1250
    public function evictTables()
1251
    {
1252
        $this->tables = array();
1253
        $this->exported = array();
1254
    }
1255
 
1256
    /**
1257
     * close
1258
     * closes the connection
1259
     *
1260
     * @return void
1261
     */
1262
    public function close()
1263
    {
1264
        $event = new Doctrine_Event($this, Doctrine_Event::CONN_CLOSE);
1265
 
1266
        $this->getAttribute(Doctrine_Core::ATTR_LISTENER)->preClose($event);
1267
 
1268
        $this->clear();
1269
 
1270
        unset($this->dbh);
1271
        $this->isConnected = false;
1272
 
1273
        $this->getAttribute(Doctrine_Core::ATTR_LISTENER)->postClose($event);
1274
    }
1275
 
1276
    /**
1277
     * get the current transaction nesting level
1278
     *
1279
     * @return integer
1280
     */
1281
    public function getTransactionLevel()
1282
    {
1283
        return $this->transaction->getTransactionLevel();
1284
    }
1285
 
1286
    /**
1287
     * errorCode
1288
     * Fetch the SQLSTATE associated with the last operation on the database handle
1289
     *
1290
     * @return integer
1291
     */
1292
    public function errorCode()
1293
    {
1294
        $this->connect();
1295
 
1296
        return $this->dbh->errorCode();
1297
    }
1298
 
1299
    /**
1300
     * errorInfo
1301
     * Fetch extended error information associated with the last operation on the database handle
1302
     *
1303
     * @return array
1304
     */
1305
    public function errorInfo()
1306
    {
1307
        $this->connect();
1308
 
1309
        return $this->dbh->errorInfo();
1310
    }
1311
 
1312
    /**
1313
     * getResultCacheDriver
1314
     *
1315
     * @return Doctrine_Cache_Interface
1316
     */
1317
    public function getResultCacheDriver()
1318
    {
1319
        if ( ! $this->getAttribute(Doctrine_Core::ATTR_RESULT_CACHE)) {
1320
            throw new Doctrine_Exception('Result Cache driver not initialized.');
1321
        }
1322
 
1323
        return $this->getAttribute(Doctrine_Core::ATTR_RESULT_CACHE);
1324
    }
1325
 
1326
    /**
1327
     * getQueryCacheDriver
1328
     *
1329
     * @return Doctrine_Cache_Interface
1330
     */
1331
    public function getQueryCacheDriver()
1332
    {
1333
        if ( ! $this->getAttribute(Doctrine_Core::ATTR_QUERY_CACHE)) {
1334
            throw new Doctrine_Exception('Query Cache driver not initialized.');
1335
        }
1336
 
1337
        return $this->getAttribute(Doctrine_Core::ATTR_QUERY_CACHE);
1338
    }
1339
 
1340
    /**
1341
     * lastInsertId
1342
     *
1343
     * Returns the ID of the last inserted row, or the last value from a sequence object,
1344
     * depending on the underlying driver.
1345
     *
1346
     * Note: This method may not return a meaningful or consistent result across different drivers,
1347
     * because the underlying database may not even support the notion of auto-increment fields or sequences.
1348
     *
1349
     * @param string $table     name of the table into which a new row was inserted
1350
     * @param string $field     name of the field into which a new row was inserted
1351
     */
1352
    public function lastInsertId($table = null, $field = null)
1353
    {
1354
        return $this->sequence->lastInsertId($table, $field);
1355
    }
1356
 
1357
    /**
1358
     * beginTransaction
1359
     * Start a transaction or set a savepoint.
1360
     *
1361
     * if trying to set a savepoint and there is no active transaction
1362
     * a new transaction is being started
1363
     *
1364
     * Listeners: onPreTransactionBegin, onTransactionBegin
1365
     *
1366
     * @param string $savepoint                 name of a savepoint to set
1367
     * @throws Doctrine_Transaction_Exception   if the transaction fails at database level
1368
     * @return integer                          current transaction nesting level
1369
     */
1370
    public function beginTransaction($savepoint = null)
1371
    {
1372
        return $this->transaction->beginTransaction($savepoint);
1373
    }
1374
 
1375
    public function beginInternalTransaction($savepoint = null)
1376
    {
1377
        return $this->transaction->beginInternalTransaction($savepoint);
1378
    }
1379
 
1380
    /**
1381
     * commit
1382
     * Commit the database changes done during a transaction that is in
1383
     * progress or release a savepoint. This function may only be called when
1384
     * auto-committing is disabled, otherwise it will fail.
1385
     *
1386
     * Listeners: onPreTransactionCommit, onTransactionCommit
1387
     *
1388
     * @param string $savepoint                 name of a savepoint to release
1389
     * @throws Doctrine_Transaction_Exception   if the transaction fails at PDO level
1390
     * @throws Doctrine_Validator_Exception     if the transaction fails due to record validations
1391
     * @return boolean                          false if commit couldn't be performed, true otherwise
1392
     */
1393
    public function commit($savepoint = null)
1394
    {
1395
        return $this->transaction->commit($savepoint);
1396
    }
1397
 
1398
    /**
1399
     * rollback
1400
     * Cancel any database changes done during a transaction or since a specific
1401
     * savepoint that is in progress. This function may only be called when
1402
     * auto-committing is disabled, otherwise it will fail. Therefore, a new
1403
     * transaction is implicitly started after canceling the pending changes.
1404
     *
1405
     * this method can be listened with onPreTransactionRollback and onTransactionRollback
1406
     * eventlistener methods
1407
     *
1408
     * @param string $savepoint                 name of a savepoint to rollback to
1409
     * @throws Doctrine_Transaction_Exception   if the rollback operation fails at database level
1410
     * @return boolean                          false if rollback couldn't be performed, true otherwise
1411
     */
1412
    public function rollback($savepoint = null)
1413
    {
1414
        return $this->transaction->rollback($savepoint);
1415
    }
1416
 
1417
    /**
1418
     * createDatabase
1419
     *
1420
     * Issue create database command for this instance of Doctrine_Connection
1421
     *
1422
     * @return string       Doctrine_Exception catched in case of failure
1423
     */
1424
    public function createDatabase()
1425
    {
1426
        if ( ! $dsn = $this->getOption('dsn')) {
1427
            throw new Doctrine_Connection_Exception('You must create your Doctrine_Connection by using a valid Doctrine style dsn in order to use the create/drop database functionality');
1428
        }
1429
 
1430
        // Parse pdo dsn so we are aware of the connection information parts
1431
        $info = $this->getManager()->parsePdoDsn($dsn);
1432
 
1433
        // Get the temporary connection to issue the create database command
1434
        $tmpConnection = $this->getTmpConnection($info);
1435
 
1436
        // Catch any exceptions and delay the throwing of it so we can close
1437
        // the tmp connection
1438
        try {
1439
            $tmpConnection->export->createDatabase($info['dbname']);
1440
        } catch (Exception $e) {}
1441
 
1442
        // Close the temporary connection used to issue the drop database command
1443
        $this->getManager()->closeConnection($tmpConnection);
1444
 
1445
        if (isset($e)) {
1446
            throw $e;
1447
        }
1448
    }
1449
 
1450
    /**
1451
     * dropDatabase
1452
     *
1453
     * Issue drop database command for this instance of Doctrine_Connection
1454
     *
1455
     * @return string       success string. Doctrine_Exception if operation failed
1456
     */
1457
    public function dropDatabase()
1458
    {
1459
        if ( ! $dsn = $this->getOption('dsn')) {
1460
            throw new Doctrine_Connection_Exception('You must create your Doctrine_Connection by using a valid Doctrine style dsn in order to use the create/drop database functionality');
1461
        }
1462
 
1463
        // Parse pdo dsn so we are aware of the connection information parts
1464
        $info = $this->getManager()->parsePdoDsn($dsn);
1465
 
1466
        // Get the temporary connection to issue the drop database command
1467
        $tmpConnection = $this->getTmpConnection($info);
1468
 
1469
        // Catch any exceptions and delay the throwing of it so we can close
1470
        // the tmp connection
1471
        try {
1472
            $tmpConnection->export->dropDatabase($info['dbname']);
1473
        } catch (Exception $e) {}
1474
 
1475
        // Close the temporary connection used to issue the drop database command
1476
        $this->getManager()->closeConnection($tmpConnection);
1477
 
1478
 
1479
        if (isset($e)) {
1480
            throw $e;
1481
        }
1482
    }
1483
 
1484
    /**
1485
     * getTmpConnection
1486
     *
1487
     * Create a temporary connection to the database with the user credentials.
1488
     * This is so the user can make a connection to a db server. Some dbms allow
1489
     * connections with no database, but some do not. In that case we have a table
1490
     * which is always guaranteed to exist. Mysql: 'mysql', PostgreSQL: 'postgres', etc.
1491
     * This value is set in the Doctrine_Export_{DRIVER} classes if required
1492
     *
1493
     * @param string $info
1494
     * @return void
1495
     */
1496
    public function getTmpConnection($info)
1497
    {
1498
        $pdoDsn = $info['scheme'] . ':';
1499
 
1500
        if ($info['unix_socket']) {
1501
            $pdoDsn .= 'unix_socket=' . $info['unix_socket'] . ';';
1502
        }
1503
 
1504
        $pdoDsn .= 'host=' . $info['host'];
1505
 
1506
        if ($info['port']) {
1507
            $pdoDsn .= ';port=' . $info['port'];
1508
        }
1509
 
1510
        if (isset($this->export->tmpConnectionDatabase) && $this->export->tmpConnectionDatabase) {
1511
            $pdoDsn .= ';dbname=' . $this->export->tmpConnectionDatabase;
1512
        }
1513
 
1514
        $username = $this->getOption('username');
1515
        $password = $this->getOption('password');
1516
 
1517
        $conn = $this->getManager()->openConnection(array($pdoDsn, $username, $password), 'doctrine_tmp_connection', false);
1518
        $conn->setOption('username', $username);
1519
        $conn->setOption('password', $password);
1520
 
1521
        return $conn;
1522
    }
1523
 
1524
    /**
1525
     * modifyLimitQuery
1526
     *
1527
     * Some dbms require specific functionality for this. Check the other connection adapters for examples
1528
     *
1529
     * @return string
1530
     */
1531
    public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false)
1532
    {
1533
        return $query;
1534
    }
1535
 
1536
    /**
1537
     * Creates dbms specific LIMIT/OFFSET SQL for the subqueries that are used in the
1538
     * context of the limit-subquery algorithm.
1539
     *
1540
     * @return string
1541
     */
1542
    public function modifyLimitSubquery(Doctrine_Table $rootTable, $query, $limit = false,
1543
            $offset = false, $isManip = false)
1544
    {
1545
        return $this->modifyLimitQuery($query, $limit, $offset, $isManip);
1546
    }
1547
 
1548
    /**
1549
     * returns a string representation of this object
1550
     * @return string
1551
     */
1552
    public function __toString()
1553
    {
1554
        return Doctrine_Lib::getConnectionAsString($this);
1555
    }
1556
 
1557
    /**
1558
     * Serialize. Remove database connection(pdo) since it cannot be serialized
1559
     *
1560
     * @return string $serialized
1561
     */
1562
    public function serialize()
1563
    {
1564
        $vars = get_object_vars($this);
1565
        $vars['dbh'] = null;
1566
        $vars['isConnected'] = false;
1567
        return serialize($vars);
1568
    }
1569
 
1570
    /**
1571
     * Unserialize. Recreate connection from serialized content
1572
     *
1573
     * @param string $serialized
1574
     * @return void
1575
     */
1576
    public function unserialize($serialized)
1577
    {
1578
        $array = unserialize($serialized);
1579
 
1580
        foreach ($array as $name => $values) {
1581
            $this->$name = $values;
1582
        }
1583
    }
1584
 
1585
    /**
1586
     * Get/generate a unique foreign key name for a relationship
1587
     *
1588
     * @param  Doctrine_Relation $relation  Relation object to generate the foreign key name for
1589
     * @return string $fkName
1590
     */
1591
    public function generateUniqueRelationForeignKeyName(Doctrine_Relation $relation)
1592
    {
1593
        $parts = array(
1594
            $relation['localTable']->getTableName(),
1595
            $relation->getLocalColumnName(),
1596
            $relation['table']->getTableName(),
1597
            $relation->getForeignColumnName(),
1598
        );
1599
        $key = implode('_', array_merge($parts, array($relation['onDelete']), array($relation['onUpdate'])));
1600
        $format = $this->getAttribute(Doctrine_Core::ATTR_FKNAME_FORMAT);
1601
 
1602
        return $this->_generateUniqueName('foreign_keys', $parts, $key, $format, $this->getAttribute(Doctrine_Core::ATTR_MAX_IDENTIFIER_LENGTH));
1603
    }
1604
 
1605
    /**
1606
     * Get/generate unique index name for a table name and set of fields
1607
     *
1608
     * @param string $tableName     The name of the table the index exists
1609
     * @param string $fields        The fields that makes up the index
1610
     * @return string $indexName    The name of the generated index
1611
     */
1612
    public function generateUniqueIndexName($tableName, $fields)
1613
    {
1614
        $fields = (array) $fields;
1615
        $parts = array($tableName);
1616
        $parts = array_merge($parts, $fields);
1617
        $key = implode('_', $parts);
1618
        $format = $this->getAttribute(Doctrine_Core::ATTR_IDXNAME_FORMAT);
1619
 
1620
        return $this->_generateUniqueName('indexes', $parts, $key, $format, $this->getAttribute(Doctrine_Core::ATTR_MAX_IDENTIFIER_LENGTH));
1621
    }
1622
 
1623
    protected function _generateUniqueName($type, $parts, $key, $format = '%s', $maxLength = null)
1624
    {
1625
        if (isset($this->_usedNames[$type][$key])) {
1626
            return $this->_usedNames[$type][$key];
1627
        }
1628
        if ($maxLength === null) {
1629
          $maxLength = $this->properties['max_identifier_length'];
1630
        }
1631
 
1632
        $generated = implode('_', $parts);
1633
 
1634
        // If the final length is greater than 64 we need to create an abbreviated fk name
1635
        if (strlen(sprintf($format, $generated)) > $maxLength) {
1636
            $generated = '';
1637
 
1638
            foreach ($parts as $part) {
1639
                $generated .= $part[0];
1640
            }
1641
 
1642
            $name = $generated;
1643
        } else {
1644
            $name = $generated;
1645
        }
1646
 
1647
        while (in_array($name, $this->_usedNames[$type])) {
1648
            $e = explode('_', $name);
1649
            $end = end($e);
1650
 
1651
            if (is_numeric($end)) {
1652
                unset($e[count($e) - 1]);
1653
                $fkName = implode('_', $e);
1654
                $name = $fkName . '_' . ++$end;
1655
            } else {
1656
                $name .= '_1';
1657
            }
1658
        }
1659
 
1660
        $this->_usedNames[$type][$key] = $name;
1661
 
1662
        return $name;
1663
    }
1664
}