Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 jpm 1
<?php
2
 
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
 
5
/**
6
 * The PEAR DB driver for PHP's pgsql extension
7
 * for interacting with PostgreSQL databases
8
 *
187 mathias 9
 * PHP version 5
94 jpm 10
 *
11
 * LICENSE: This source file is subject to version 3.0 of the PHP license
12
 * that is available through the world-wide-web at the following URI:
13
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
14
 * the PHP License and are unable to obtain it through the web, please
15
 * send a note to license@php.net so we can mail you a copy immediately.
16
 *
17
 * @category   Database
18
 * @package    DB
19
 * @author     Rui Hirokawa <hirokawa@php.net>
20
 * @author     Stig Bakken <ssb@php.net>
21
 * @author     Daniel Convissor <danielc@php.net>
187 mathias 22
 * @copyright  1997-2007 The PHP Group
94 jpm 23
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
187 mathias 24
 * @version    CVS: $Id$
94 jpm 25
 * @link       http://pear.php.net/package/DB
26
 */
27
 
28
/**
29
 * Obtain the DB_common class so it can be extended from
30
 */
31
require_once 'DB/common.php';
32
 
33
/**
34
 * The methods PEAR DB uses to interact with PHP's pgsql extension
35
 * for interacting with PostgreSQL databases
36
 *
37
 * These methods overload the ones declared in DB_common.
38
 *
39
 * @category   Database
40
 * @package    DB
41
 * @author     Rui Hirokawa <hirokawa@php.net>
42
 * @author     Stig Bakken <ssb@php.net>
43
 * @author     Daniel Convissor <danielc@php.net>
187 mathias 44
 * @copyright  1997-2007 The PHP Group
94 jpm 45
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
187 mathias 46
 * @version    Release: 1.9.2
94 jpm 47
 * @link       http://pear.php.net/package/DB
48
 */
49
class DB_pgsql extends DB_common
50
{
51
    // {{{ properties
52
 
53
    /**
54
     * The DB driver type (mysql, oci8, odbc, etc.)
55
     * @var string
56
     */
57
    var $phptype = 'pgsql';
58
 
59
    /**
60
     * The database syntax variant to be used (db2, access, etc.), if any
61
     * @var string
62
     */
63
    var $dbsyntax = 'pgsql';
64
 
65
    /**
66
     * The capabilities of this DB implementation
67
     *
68
     * The 'new_link' element contains the PHP version that first provided
69
     * new_link support for this DBMS.  Contains false if it's unsupported.
70
     *
71
     * Meaning of the 'limit' element:
72
     *   + 'emulate' = emulate with fetch row by number
73
     *   + 'alter'   = alter the query
74
     *   + false     = skip rows
75
     *
76
     * @var array
77
     */
78
    var $features = array(
79
        'limit'         => 'alter',
80
        'new_link'      => '4.3.0',
81
        'numrows'       => true,
82
        'pconnect'      => true,
83
        'prepare'       => false,
84
        'ssl'           => true,
85
        'transactions'  => true,
86
    );
87
 
88
    /**
89
     * A mapping of native error codes to DB error codes
90
     * @var array
91
     */
92
    var $errorcode_map = array(
93
    );
94
 
95
    /**
96
     * The raw database connection created by PHP
97
     * @var resource
98
     */
99
    var $connection;
100
 
101
    /**
102
     * The DSN information for connecting to a database
103
     * @var array
104
     */
105
    var $dsn = array();
106
 
107
 
108
    /**
109
     * Should data manipulation queries be committed automatically?
110
     * @var bool
111
     * @access private
112
     */
113
    var $autocommit = true;
114
 
115
    /**
116
     * The quantity of transactions begun
117
     *
118
     * {@internal  While this is private, it can't actually be designated
119
     * private in PHP 5 because it is directly accessed in the test suite.}}
120
     *
121
     * @var integer
122
     * @access private
123
     */
124
    var $transaction_opcount = 0;
125
 
126
    /**
127
     * The number of rows affected by a data manipulation query
128
     * @var integer
129
     */
130
    var $affected = 0;
131
 
132
    /**
133
     * The current row being looked at in fetchInto()
134
     * @var array
135
     * @access private
136
     */
137
    var $row = array();
138
 
139
    /**
140
     * The number of rows in a given result set
141
     * @var array
142
     * @access private
143
     */
144
    var $_num_rows = array();
145
 
146
 
147
    // }}}
148
    // {{{ constructor
149
 
150
    /**
187 mathias 151
     * This constructor calls <kbd>parent::__construct()</kbd>
94 jpm 152
     *
153
     * @return void
154
     */
187 mathias 155
    function __construct()
94 jpm 156
    {
187 mathias 157
        parent::__construct();
94 jpm 158
    }
159
 
160
    // }}}
161
    // {{{ connect()
162
 
163
    /**
164
     * Connect to the database server, log in and open the database
165
     *
166
     * Don't call this method directly.  Use DB::connect() instead.
167
     *
168
     * PEAR DB's pgsql driver supports the following extra DSN options:
169
     *   + connect_timeout  How many seconds to wait for a connection to
170
     *                       be established.  Available since PEAR DB 1.7.0.
171
     *   + new_link         If set to true, causes subsequent calls to
172
     *                       connect() to return a new connection link
173
     *                       instead of the existing one.  WARNING: this is
174
     *                       not portable to other DBMS's.  Available only
175
     *                       if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0.
176
     *   + options          Command line options to be sent to the server.
177
     *                       Available since PEAR DB 1.6.4.
178
     *   + service          Specifies a service name in pg_service.conf that
179
     *                       holds additional connection parameters.
180
     *                       Available since PEAR DB 1.7.0.
181
     *   + sslmode          How should SSL be used when connecting?  Values:
182
     *                       disable, allow, prefer or require.
183
     *                       Available since PEAR DB 1.7.0.
184
     *   + tty              This was used to specify where to send server
185
     *                       debug output.  Available since PEAR DB 1.6.4.
186
     *
187
     * Example of connecting to a new link via a socket:
188
     * <code>
189
     * require_once 'DB.php';
190
     *
191
     * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true';
192
     * $options = array(
193
     *     'portability' => DB_PORTABILITY_ALL,
194
     * );
195
     *
187 mathias 196
     * $db = DB::connect($dsn, $options);
94 jpm 197
     * if (PEAR::isError($db)) {
198
     *     die($db->getMessage());
199
     * }
200
     * </code>
201
     *
202
     * @param array $dsn         the data source name
203
     * @param bool  $persistent  should the connection be persistent?
204
     *
205
     * @return int  DB_OK on success. A DB_Error object on failure.
206
     *
207
     * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT
208
     */
209
    function connect($dsn, $persistent = false)
210
    {
211
        if (!PEAR::loadExtension('pgsql')) {
212
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
213
        }
214
 
215
        $this->dsn = $dsn;
216
        if ($dsn['dbsyntax']) {
217
            $this->dbsyntax = $dsn['dbsyntax'];
218
        }
219
 
220
        $protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp';
221
 
222
        $params = array('');
223
        if ($protocol == 'tcp') {
224
            if ($dsn['hostspec']) {
225
                $params[0] .= 'host=' . $dsn['hostspec'];
226
            }
227
            if ($dsn['port']) {
228
                $params[0] .= ' port=' . $dsn['port'];
229
            }
230
        } elseif ($protocol == 'unix') {
231
            // Allow for pg socket in non-standard locations.
232
            if ($dsn['socket']) {
233
                $params[0] .= 'host=' . $dsn['socket'];
234
            }
235
            if ($dsn['port']) {
236
                $params[0] .= ' port=' . $dsn['port'];
237
            }
238
        }
239
        if ($dsn['database']) {
240
            $params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\'';
241
        }
242
        if ($dsn['username']) {
243
            $params[0] .= ' user=\'' . addslashes($dsn['username']) . '\'';
244
        }
245
        if ($dsn['password']) {
246
            $params[0] .= ' password=\'' . addslashes($dsn['password']) . '\'';
247
        }
248
        if (!empty($dsn['options'])) {
249
            $params[0] .= ' options=' . $dsn['options'];
250
        }
251
        if (!empty($dsn['tty'])) {
252
            $params[0] .= ' tty=' . $dsn['tty'];
253
        }
254
        if (!empty($dsn['connect_timeout'])) {
255
            $params[0] .= ' connect_timeout=' . $dsn['connect_timeout'];
256
        }
257
        if (!empty($dsn['sslmode'])) {
258
            $params[0] .= ' sslmode=' . $dsn['sslmode'];
259
        }
260
        if (!empty($dsn['service'])) {
261
            $params[0] .= ' service=' . $dsn['service'];
262
        }
263
 
264
        if (isset($dsn['new_link'])
265
            && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
266
        {
267
            if (version_compare(phpversion(), '4.3.0', '>=')) {
268
                $params[] = PGSQL_CONNECT_FORCE_NEW;
269
            }
270
        }
271
 
272
        $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
273
 
274
        $ini = ini_get('track_errors');
275
        $php_errormsg = '';
276
        if ($ini) {
277
            $this->connection = @call_user_func_array($connect_function,
278
                                                      $params);
279
        } else {
187 mathias 280
            @ini_set('track_errors', 1);
94 jpm 281
            $this->connection = @call_user_func_array($connect_function,
282
                                                      $params);
187 mathias 283
            @ini_set('track_errors', $ini);
94 jpm 284
        }
285
 
286
        if (!$this->connection) {
287
            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
288
                                     null, null, null,
289
                                     $php_errormsg);
290
        }
291
        return DB_OK;
292
    }
293
 
294
    // }}}
295
    // {{{ disconnect()
296
 
297
    /**
298
     * Disconnects from the database server
299
     *
300
     * @return bool  TRUE on success, FALSE on failure
301
     */
302
    function disconnect()
303
    {
304
        $ret = @pg_close($this->connection);
305
        $this->connection = null;
306
        return $ret;
307
    }
308
 
309
    // }}}
310
    // {{{ simpleQuery()
311
 
312
    /**
313
     * Sends a query to the database server
314
     *
315
     * @param string  the SQL query string
316
     *
317
     * @return mixed  + a PHP result resrouce for successful SELECT queries
318
     *                + the DB_OK constant for other successful queries
319
     *                + a DB_Error object on failure
320
     */
321
    function simpleQuery($query)
322
    {
187 mathias 323
        $ismanip = $this->_checkManip($query);
94 jpm 324
        $this->last_query = $query;
325
        $query = $this->modifyQuery($query);
326
        if (!$this->autocommit && $ismanip) {
327
            if ($this->transaction_opcount == 0) {
328
                $result = @pg_exec($this->connection, 'begin;');
329
                if (!$result) {
330
                    return $this->pgsqlRaiseError();
331
                }
332
            }
333
            $this->transaction_opcount++;
334
        }
335
        $result = @pg_exec($this->connection, $query);
336
        if (!$result) {
337
            return $this->pgsqlRaiseError();
338
        }
187 mathias 339
 
340
        /*
341
         * Determine whether queries produce affected rows, result or nothing.
342
         *
343
         * This logic was introduced in version 1.1 of the file by ssb,
344
         * though the regex has been modified slightly since then.
345
         *
346
         * PostgreSQL commands:
347
         * ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
348
         * CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
349
         * GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
350
         * REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
351
         * UNLISTEN, UPDATE, VACUUM, WITH
352
         */
94 jpm 353
        if ($ismanip) {
354
            $this->affected = @pg_affected_rows($result);
355
            return DB_OK;
187 mathias 356
        } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|FETCH|SHOW|WITH)\s/si',
357
                             $query))
358
        {
94 jpm 359
            $this->row[(int)$result] = 0; // reset the row counter.
360
            $numrows = $this->numRows($result);
361
            if (is_object($numrows)) {
362
                return $numrows;
363
            }
364
            $this->_num_rows[(int)$result] = $numrows;
365
            $this->affected = 0;
366
            return $result;
367
        } else {
368
            $this->affected = 0;
369
            return DB_OK;
370
        }
371
    }
372
 
373
    // }}}
374
    // {{{ nextResult()
375
 
376
    /**
377
     * Move the internal pgsql result pointer to the next available result
378
     *
379
     * @param a valid fbsql result resource
380
     *
381
     * @access public
382
     *
383
     * @return true if a result is available otherwise return false
384
     */
385
    function nextResult($result)
386
    {
387
        return false;
388
    }
389
 
390
    // }}}
391
    // {{{ fetchInto()
392
 
393
    /**
394
     * Places a row from the result set into the given array
395
     *
396
     * Formating of the array and the data therein are configurable.
397
     * See DB_result::fetchInto() for more information.
398
     *
399
     * This method is not meant to be called directly.  Use
400
     * DB_result::fetchInto() instead.  It can't be declared "protected"
401
     * because DB_result is a separate object.
402
     *
403
     * @param resource $result    the query result resource
404
     * @param array    $arr       the referenced array to put the data in
405
     * @param int      $fetchmode how the resulting array should be indexed
406
     * @param int      $rownum    the row number to fetch (0 = first row)
407
     *
408
     * @return mixed  DB_OK on success, NULL when the end of a result set is
409
     *                 reached or on failure
410
     *
411
     * @see DB_result::fetchInto()
412
     */
413
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
414
    {
415
        $result_int = (int)$result;
416
        $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int];
417
        if ($rownum >= $this->_num_rows[$result_int]) {
418
            return null;
419
        }
420
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
421
            $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC);
422
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
423
                $arr = array_change_key_case($arr, CASE_LOWER);
424
            }
425
        } else {
426
            $arr = @pg_fetch_row($result, $rownum);
427
        }
428
        if (!$arr) {
429
            return null;
430
        }
431
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
432
            $this->_rtrimArrayValues($arr);
433
        }
434
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
435
            $this->_convertNullArrayValuesToEmpty($arr);
436
        }
437
        $this->row[$result_int] = ++$rownum;
438
        return DB_OK;
439
    }
440
 
441
    // }}}
442
    // {{{ freeResult()
443
 
444
    /**
445
     * Deletes the result set and frees the memory occupied by the result set
446
     *
447
     * This method is not meant to be called directly.  Use
448
     * DB_result::free() instead.  It can't be declared "protected"
449
     * because DB_result is a separate object.
450
     *
451
     * @param resource $result  PHP's query result resource
452
     *
453
     * @return bool  TRUE on success, FALSE if $result is invalid
454
     *
455
     * @see DB_result::free()
456
     */
457
    function freeResult($result)
458
    {
459
        if (is_resource($result)) {
460
            unset($this->row[(int)$result]);
461
            unset($this->_num_rows[(int)$result]);
462
            $this->affected = 0;
463
            return @pg_freeresult($result);
464
        }
465
        return false;
466
    }
467
 
468
    // }}}
187 mathias 469
    // {{{ quoteBoolean()
94 jpm 470
 
471
    /**
187 mathias 472
     * Formats a boolean value for use within a query in a locale-independent
473
     * manner.
94 jpm 474
     *
187 mathias 475
     * @param boolean the boolean value to be quoted.
476
     * @return string the quoted string.
94 jpm 477
     * @see DB_common::quoteSmart()
187 mathias 478
     * @since Method available since release 1.7.8.
94 jpm 479
     */
187 mathias 480
    function quoteBoolean($boolean) {
481
        return $boolean ? 'TRUE' : 'FALSE';
94 jpm 482
    }
187 mathias 483
 
94 jpm 484
    // }}}
485
    // {{{ escapeSimple()
486
 
487
    /**
488
     * Escapes a string according to the current DBMS's standards
489
     *
490
     * {@internal PostgreSQL treats a backslash as an escape character,
491
     * so they are escaped as well.
492
     *
493
     * @param string $str  the string to be escaped
494
     *
495
     * @return string  the escaped string
496
     *
497
     * @see DB_common::quoteSmart()
498
     * @since Method available since Release 1.6.0
499
     */
500
    function escapeSimple($str)
501
    {
187 mathias 502
        if (function_exists('pg_escape_string')) {
503
            /* This fixes an undocumented BC break in PHP 5.2.0 which changed
504
             * the prototype of pg_escape_string. I'm not thrilled about having
505
             * to sniff the PHP version, quite frankly, but it's the only way
506
             * to deal with the problem. Revision 1.331.2.13.2.10 on
507
             * php-src/ext/pgsql/pgsql.c (PHP_5_2 branch) is to blame, for the
508
             * record. */
509
            if (version_compare(PHP_VERSION, '5.2.0', '>=')) {
510
                return pg_escape_string($this->connection, $str);
511
            } else {
512
                return pg_escape_string($str);
513
            }
514
        } else {
515
            return str_replace("'", "''", str_replace('\\', '\\\\', $str));
516
        }
94 jpm 517
    }
518
 
519
    // }}}
520
    // {{{ numCols()
521
 
522
    /**
523
     * Gets the number of columns in a result set
524
     *
525
     * This method is not meant to be called directly.  Use
526
     * DB_result::numCols() instead.  It can't be declared "protected"
527
     * because DB_result is a separate object.
528
     *
529
     * @param resource $result  PHP's query result resource
530
     *
531
     * @return int  the number of columns.  A DB_Error object on failure.
532
     *
533
     * @see DB_result::numCols()
534
     */
535
    function numCols($result)
536
    {
537
        $cols = @pg_numfields($result);
538
        if (!$cols) {
539
            return $this->pgsqlRaiseError();
540
        }
541
        return $cols;
542
    }
543
 
544
    // }}}
545
    // {{{ numRows()
546
 
547
    /**
548
     * Gets the number of rows in a result set
549
     *
550
     * This method is not meant to be called directly.  Use
551
     * DB_result::numRows() instead.  It can't be declared "protected"
552
     * because DB_result is a separate object.
553
     *
554
     * @param resource $result  PHP's query result resource
555
     *
556
     * @return int  the number of rows.  A DB_Error object on failure.
557
     *
558
     * @see DB_result::numRows()
559
     */
560
    function numRows($result)
561
    {
562
        $rows = @pg_numrows($result);
563
        if ($rows === null) {
564
            return $this->pgsqlRaiseError();
565
        }
566
        return $rows;
567
    }
568
 
569
    // }}}
570
    // {{{ autoCommit()
571
 
572
    /**
573
     * Enables or disables automatic commits
574
     *
575
     * @param bool $onoff  true turns it on, false turns it off
576
     *
577
     * @return int  DB_OK on success.  A DB_Error object if the driver
578
     *               doesn't support auto-committing transactions.
579
     */
580
    function autoCommit($onoff = false)
581
    {
582
        // XXX if $this->transaction_opcount > 0, we should probably
583
        // issue a warning here.
584
        $this->autocommit = $onoff ? true : false;
585
        return DB_OK;
586
    }
587
 
588
    // }}}
589
    // {{{ commit()
590
 
591
    /**
592
     * Commits the current transaction
593
     *
594
     * @return int  DB_OK on success.  A DB_Error object on failure.
595
     */
596
    function commit()
597
    {
598
        if ($this->transaction_opcount > 0) {
599
            // (disabled) hack to shut up error messages from libpq.a
600
            //@fclose(@fopen("php://stderr", "w"));
601
            $result = @pg_exec($this->connection, 'end;');
602
            $this->transaction_opcount = 0;
603
            if (!$result) {
604
                return $this->pgsqlRaiseError();
605
            }
606
        }
607
        return DB_OK;
608
    }
609
 
610
    // }}}
611
    // {{{ rollback()
612
 
613
    /**
614
     * Reverts the current transaction
615
     *
616
     * @return int  DB_OK on success.  A DB_Error object on failure.
617
     */
618
    function rollback()
619
    {
620
        if ($this->transaction_opcount > 0) {
621
            $result = @pg_exec($this->connection, 'abort;');
622
            $this->transaction_opcount = 0;
623
            if (!$result) {
624
                return $this->pgsqlRaiseError();
625
            }
626
        }
627
        return DB_OK;
628
    }
629
 
630
    // }}}
631
    // {{{ affectedRows()
632
 
633
    /**
634
     * Determines the number of rows affected by a data maniuplation query
635
     *
636
     * 0 is returned for queries that don't manipulate data.
637
     *
638
     * @return int  the number of rows.  A DB_Error object on failure.
639
     */
640
    function affectedRows()
641
    {
642
        return $this->affected;
643
    }
644
 
645
    // }}}
646
    // {{{ nextId()
647
 
648
    /**
649
     * Returns the next free id in a sequence
650
     *
651
     * @param string  $seq_name  name of the sequence
652
     * @param boolean $ondemand  when true, the seqence is automatically
653
     *                            created if it does not exist
654
     *
655
     * @return int  the next id number in the sequence.
656
     *               A DB_Error object on failure.
657
     *
658
     * @see DB_common::nextID(), DB_common::getSequenceName(),
659
     *      DB_pgsql::createSequence(), DB_pgsql::dropSequence()
660
     */
661
    function nextId($seq_name, $ondemand = true)
662
    {
663
        $seqname = $this->getSequenceName($seq_name);
664
        $repeat = false;
665
        do {
666
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
187 mathias 667
            $result = $this->query("SELECT NEXTVAL('${seqname}')");
94 jpm 668
            $this->popErrorHandling();
669
            if ($ondemand && DB::isError($result) &&
670
                $result->getCode() == DB_ERROR_NOSUCHTABLE) {
671
                $repeat = true;
672
                $this->pushErrorHandling(PEAR_ERROR_RETURN);
673
                $result = $this->createSequence($seq_name);
674
                $this->popErrorHandling();
675
                if (DB::isError($result)) {
676
                    return $this->raiseError($result);
677
                }
678
            } else {
679
                $repeat = false;
680
            }
681
        } while ($repeat);
682
        if (DB::isError($result)) {
683
            return $this->raiseError($result);
684
        }
685
        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
686
        $result->free();
687
        return $arr[0];
688
    }
689
 
690
    // }}}
691
    // {{{ createSequence()
692
 
693
    /**
694
     * Creates a new sequence
695
     *
696
     * @param string $seq_name  name of the new sequence
697
     *
698
     * @return int  DB_OK on success.  A DB_Error object on failure.
699
     *
700
     * @see DB_common::createSequence(), DB_common::getSequenceName(),
701
     *      DB_pgsql::nextID(), DB_pgsql::dropSequence()
702
     */
703
    function createSequence($seq_name)
704
    {
705
        $seqname = $this->getSequenceName($seq_name);
706
        $result = $this->query("CREATE SEQUENCE ${seqname}");
707
        return $result;
708
    }
709
 
710
    // }}}
711
    // {{{ dropSequence()
712
 
713
    /**
714
     * Deletes a sequence
715
     *
716
     * @param string $seq_name  name of the sequence to be deleted
717
     *
718
     * @return int  DB_OK on success.  A DB_Error object on failure.
719
     *
720
     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
721
     *      DB_pgsql::nextID(), DB_pgsql::createSequence()
722
     */
723
    function dropSequence($seq_name)
724
    {
725
        return $this->query('DROP SEQUENCE '
726
                            . $this->getSequenceName($seq_name));
727
    }
728
 
729
    // }}}
730
    // {{{ modifyLimitQuery()
731
 
732
    /**
733
     * Adds LIMIT clauses to a query string according to current DBMS standards
734
     *
735
     * @param string $query   the query to modify
736
     * @param int    $from    the row to start to fetching (0 = the first row)
737
     * @param int    $count   the numbers of rows to fetch
738
     * @param mixed  $params  array, string or numeric data to be used in
739
     *                         execution of the statement.  Quantity of items
740
     *                         passed must match quantity of placeholders in
741
     *                         query:  meaning 1 placeholder for non-array
742
     *                         parameters or 1 placeholder per array element.
743
     *
744
     * @return string  the query string with LIMIT clauses added
745
     *
746
     * @access protected
747
     */
748
    function modifyLimitQuery($query, $from, $count, $params = array())
749
    {
750
        return "$query LIMIT $count OFFSET $from";
751
    }
752
 
753
    // }}}
754
    // {{{ pgsqlRaiseError()
755
 
756
    /**
757
     * Produces a DB_Error object regarding the current problem
758
     *
759
     * @param int $errno  if the error is being manually raised pass a
760
     *                     DB_ERROR* constant here.  If this isn't passed
761
     *                     the error information gathered from the DBMS.
762
     *
763
     * @return object  the DB_Error object
764
     *
765
     * @see DB_common::raiseError(),
766
     *      DB_pgsql::errorNative(), DB_pgsql::errorCode()
767
     */
768
    function pgsqlRaiseError($errno = null)
769
    {
770
        $native = $this->errorNative();
187 mathias 771
        if (!$native) {
772
            $native = 'Database connection has been lost.';
773
            $errno = DB_ERROR_CONNECT_FAILED;
774
        }
94 jpm 775
        if ($errno === null) {
776
            $errno = $this->errorCode($native);
777
        }
778
        return $this->raiseError($errno, null, null, null, $native);
779
    }
780
 
781
    // }}}
782
    // {{{ errorNative()
783
 
784
    /**
785
     * Gets the DBMS' native error message produced by the last query
786
     *
787
     * {@internal Error messages are used instead of error codes
788
     * in order to support older versions of PostgreSQL.}}
789
     *
790
     * @return string  the DBMS' error message
791
     */
792
    function errorNative()
793
    {
794
        return @pg_errormessage($this->connection);
795
    }
796
 
797
    // }}}
798
    // {{{ errorCode()
799
 
800
    /**
801
     * Determines PEAR::DB error code from the database's text error message.
802
     *
803
     * @param  string  $errormsg  error message returned from the database
804
     * @return integer  an error number from a DB error constant
805
     */
806
    function errorCode($errormsg)
807
    {
808
        static $error_regexps;
809
        if (!isset($error_regexps)) {
810
            $error_regexps = array(
187 mathias 811
                '/column .* (of relation .*)?does not exist/i'
812
                    => DB_ERROR_NOSUCHFIELD,
94 jpm 813
                '/(relation|sequence|table).*does not exist|class .* not found/i'
814
                    => DB_ERROR_NOSUCHTABLE,
815
                '/index .* does not exist/'
816
                    => DB_ERROR_NOT_FOUND,
817
                '/relation .* already exists/i'
818
                    => DB_ERROR_ALREADY_EXISTS,
819
                '/(divide|division) by zero$/i'
820
                    => DB_ERROR_DIVZERO,
821
                '/pg_atoi: error in .*: can\'t parse /i'
822
                    => DB_ERROR_INVALID_NUMBER,
823
                '/invalid input syntax for( type)? (integer|numeric)/i'
824
                    => DB_ERROR_INVALID_NUMBER,
825
                '/value .* is out of range for type \w*int/i'
826
                    => DB_ERROR_INVALID_NUMBER,
827
                '/integer out of range/i'
828
                    => DB_ERROR_INVALID_NUMBER,
829
                '/value too long for type character/i'
830
                    => DB_ERROR_INVALID,
831
                '/attribute .* not found|relation .* does not have attribute/i'
832
                    => DB_ERROR_NOSUCHFIELD,
833
                '/column .* specified in USING clause does not exist in (left|right) table/i'
834
                    => DB_ERROR_NOSUCHFIELD,
835
                '/parser: parse error at or near/i'
836
                    => DB_ERROR_SYNTAX,
837
                '/syntax error at/'
838
                    => DB_ERROR_SYNTAX,
839
                '/column reference .* is ambiguous/i'
840
                    => DB_ERROR_SYNTAX,
841
                '/permission denied/'
842
                    => DB_ERROR_ACCESS_VIOLATION,
843
                '/violates not-null constraint/'
844
                    => DB_ERROR_CONSTRAINT_NOT_NULL,
845
                '/violates [\w ]+ constraint/'
846
                    => DB_ERROR_CONSTRAINT,
847
                '/referential integrity violation/'
848
                    => DB_ERROR_CONSTRAINT,
849
                '/more expressions than target columns/i'
850
                    => DB_ERROR_VALUE_COUNT_ON_ROW,
851
            );
852
        }
853
        foreach ($error_regexps as $regexp => $code) {
854
            if (preg_match($regexp, $errormsg)) {
855
                return $code;
856
            }
857
        }
858
        // Fall back to DB_ERROR if there was no mapping.
859
        return DB_ERROR;
860
    }
861
 
862
    // }}}
863
    // {{{ tableInfo()
864
 
865
    /**
866
     * Returns information about a table or a result set
867
     *
868
     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
869
     * is a table name.
870
     *
871
     * @param object|string  $result  DB_result object from a query or a
872
     *                                 string containing the name of a table.
873
     *                                 While this also accepts a query result
874
     *                                 resource identifier, this behavior is
875
     *                                 deprecated.
876
     * @param int            $mode    a valid tableInfo mode
877
     *
878
     * @return array  an associative array with the information requested.
879
     *                 A DB_Error object on failure.
880
     *
881
     * @see DB_common::tableInfo()
882
     */
883
    function tableInfo($result, $mode = null)
884
    {
885
        if (is_string($result)) {
886
            /*
887
             * Probably received a table name.
888
             * Create a result resource identifier.
889
             */
890
            $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
891
            $got_string = true;
892
        } elseif (isset($result->result)) {
893
            /*
894
             * Probably received a result object.
895
             * Extract the result resource identifier.
896
             */
897
            $id = $result->result;
898
            $got_string = false;
899
        } else {
900
            /*
901
             * Probably received a result resource identifier.
902
             * Copy it.
903
             * Deprecated.  Here for compatibility only.
904
             */
905
            $id = $result;
906
            $got_string = false;
907
        }
908
 
909
        if (!is_resource($id)) {
910
            return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
911
        }
912
 
913
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
914
            $case_func = 'strtolower';
915
        } else {
916
            $case_func = 'strval';
917
        }
918
 
919
        $count = @pg_numfields($id);
920
        $res   = array();
921
 
922
        if ($mode) {
923
            $res['num_fields'] = $count;
924
        }
925
 
926
        for ($i = 0; $i < $count; $i++) {
927
            $res[$i] = array(
928
                'table' => $got_string ? $case_func($result) : '',
929
                'name'  => $case_func(@pg_fieldname($id, $i)),
930
                'type'  => @pg_fieldtype($id, $i),
931
                'len'   => @pg_fieldsize($id, $i),
932
                'flags' => $got_string
933
                           ? $this->_pgFieldFlags($id, $i, $result)
934
                           : '',
935
            );
936
            if ($mode & DB_TABLEINFO_ORDER) {
937
                $res['order'][$res[$i]['name']] = $i;
938
            }
939
            if ($mode & DB_TABLEINFO_ORDERTABLE) {
940
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
941
            }
942
        }
943
 
944
        // free the result only if we were called on a table
945
        if ($got_string) {
946
            @pg_freeresult($id);
947
        }
948
        return $res;
949
    }
950
 
951
    // }}}
952
    // {{{ _pgFieldFlags()
953
 
954
    /**
955
     * Get a column's flags
956
     *
957
     * Supports "not_null", "default_value", "primary_key", "unique_key"
958
     * and "multiple_key".  The default value is passed through
959
     * rawurlencode() in case there are spaces in it.
960
     *
961
     * @param int $resource   the PostgreSQL result identifier
962
     * @param int $num_field  the field number
963
     *
964
     * @return string  the flags
965
     *
966
     * @access private
967
     */
968
    function _pgFieldFlags($resource, $num_field, $table_name)
969
    {
970
        $field_name = @pg_fieldname($resource, $num_field);
971
 
187 mathias 972
        // Check if there's a schema in $table_name and update things
973
        // accordingly.
974
        $from = 'pg_attribute f, pg_class tab, pg_type typ';
975
        if (strpos($table_name, '.') !== false) {
976
            $from .= ', pg_namespace nsp';
977
            list($schema, $table) = explode('.', $table_name);
978
            $tableWhere = "tab.relname = '$table' AND tab.relnamespace = nsp.oid AND nsp.nspname = '$schema'";
979
        } else {
980
            $tableWhere = "tab.relname = '$table_name'";
981
        }
982
 
94 jpm 983
        $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
187 mathias 984
                                FROM $from
94 jpm 985
                                WHERE tab.relname = typ.typname
986
                                AND typ.typrelid = f.attrelid
987
                                AND f.attname = '$field_name'
187 mathias 988
                                AND $tableWhere");
94 jpm 989
        if (@pg_numrows($result) > 0) {
990
            $row = @pg_fetch_row($result, 0);
991
            $flags  = ($row[0] == 't') ? 'not_null ' : '';
992
 
993
            if ($row[1] == 't') {
994
                $result = @pg_exec($this->connection, "SELECT a.adsrc
187 mathias 995
                                    FROM $from, pg_attrdef a
94 jpm 996
                                    WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
997
                                    AND f.attrelid = a.adrelid AND f.attname = '$field_name'
187 mathias 998
                                    AND $tableWhere AND f.attnum = a.adnum");
94 jpm 999
                $row = @pg_fetch_row($result, 0);
1000
                $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]);
1001
                $flags .= 'default_' . rawurlencode($num) . ' ';
1002
            }
1003
        } else {
1004
            $flags = '';
1005
        }
1006
        $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
187 mathias 1007
                                FROM $from, pg_index i
94 jpm 1008
                                WHERE tab.relname = typ.typname
1009
                                AND typ.typrelid = f.attrelid
1010
                                AND f.attrelid = i.indrelid
1011
                                AND f.attname = '$field_name'
187 mathias 1012
                                AND $tableWhere");
94 jpm 1013
        $count = @pg_numrows($result);
1014
 
1015
        for ($i = 0; $i < $count ; $i++) {
1016
            $row = @pg_fetch_row($result, $i);
1017
            $keys = explode(' ', $row[2]);
1018
 
1019
            if (in_array($num_field + 1, $keys)) {
1020
                $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : '';
1021
                $flags .= ($row[1] == 't') ? 'primary_key ' : '';
1022
                if (count($keys) > 1)
1023
                    $flags .= 'multiple_key ';
1024
            }
1025
        }
1026
 
1027
        return trim($flags);
1028
    }
1029
 
1030
    // }}}
1031
    // {{{ getSpecialQuery()
1032
 
1033
    /**
1034
     * Obtains the query string needed for listing a given type of objects
1035
     *
1036
     * @param string $type  the kind of objects you want to retrieve
1037
     *
1038
     * @return string  the SQL query string or null if the driver doesn't
1039
     *                  support the object type requested
1040
     *
1041
     * @access protected
1042
     * @see DB_common::getListOf()
1043
     */
1044
    function getSpecialQuery($type)
1045
    {
1046
        switch ($type) {
1047
            case 'tables':
1048
                return 'SELECT c.relname AS "Name"'
1049
                        . ' FROM pg_class c, pg_user u'
1050
                        . ' WHERE c.relowner = u.usesysid'
1051
                        . " AND c.relkind = 'r'"
1052
                        . ' AND NOT EXISTS'
1053
                        . ' (SELECT 1 FROM pg_views'
1054
                        . '  WHERE viewname = c.relname)'
1055
                        . " AND c.relname !~ '^(pg_|sql_)'"
1056
                        . ' UNION'
1057
                        . ' SELECT c.relname AS "Name"'
1058
                        . ' FROM pg_class c'
1059
                        . " WHERE c.relkind = 'r'"
1060
                        . ' AND NOT EXISTS'
1061
                        . ' (SELECT 1 FROM pg_views'
1062
                        . '  WHERE viewname = c.relname)'
1063
                        . ' AND NOT EXISTS'
1064
                        . ' (SELECT 1 FROM pg_user'
1065
                        . '  WHERE usesysid = c.relowner)'
1066
                        . " AND c.relname !~ '^pg_'";
1067
            case 'schema.tables':
1068
                return "SELECT schemaname || '.' || tablename"
1069
                        . ' AS "Name"'
1070
                        . ' FROM pg_catalog.pg_tables'
1071
                        . ' WHERE schemaname NOT IN'
1072
                        . " ('pg_catalog', 'information_schema', 'pg_toast')";
187 mathias 1073
            case 'schema.views':
1074
                return "SELECT schemaname || '.' || viewname from pg_views WHERE schemaname"
1075
                        . " NOT IN ('information_schema', 'pg_catalog')";
94 jpm 1076
            case 'views':
1077
                // Table cols: viewname | viewowner | definition
1078
                return 'SELECT viewname from pg_views WHERE schemaname'
1079
                        . " NOT IN ('information_schema', 'pg_catalog')";
1080
            case 'users':
1081
                // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd  |valuntil
1082
                return 'SELECT usename FROM pg_user';
1083
            case 'databases':
1084
                return 'SELECT datname FROM pg_database';
1085
            case 'functions':
1086
            case 'procedures':
1087
                return 'SELECT proname FROM pg_proc WHERE proowner <> 1';
1088
            default:
1089
                return null;
1090
        }
1091
    }
1092
 
1093
    // }}}
187 mathias 1094
    // {{{ _checkManip()
94 jpm 1095
 
187 mathias 1096
    /**
1097
     * Checks if the given query is a manipulation query. This also takes into
1098
     * account the _next_query_manip flag and sets the _last_query_manip flag
1099
     * (and resets _next_query_manip) according to the result.
1100
     *
1101
     * @param string The query to check.
1102
     *
1103
     * @return boolean true if the query is a manipulation query, false
1104
     * otherwise
1105
     *
1106
     * @access protected
1107
     */
1108
    function _checkManip($query)
1109
    {
1110
        return (preg_match('/^\s*(SAVEPOINT|RELEASE)\s+/i', $query)
1111
                || parent::_checkManip($query));
1112
    }
1113
 
94 jpm 1114
}
1115
 
1116
/*
1117
 * Local variables:
1118
 * tab-width: 4
1119
 * c-basic-offset: 4
1120
 * End:
1121
 */
1122
 
1123
?>