source: branches/feature-module-update/data/module/DB/pgsql.php @ 15532

Revision 15532, 34.9 KB checked in by nanasess, 17 years ago (diff)

svn:mime-type 修正

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