source: temp/trunk/data/module/DB/pgsql.php @ 6113

Revision 6113, 34.9 KB checked in by naka, 20 years ago (diff)

* empty log message *

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