]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/DB/pgsql.php
Merge branch '0.9.x' of git://gitorious.org/statusnet/mainline into 0.9.x
[quix0rs-gnu-social.git] / extlib / DB / pgsql.php
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-2007 The PHP Group
23  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
24  * @version    CVS: $Id: pgsql.php,v 1.139 2007/11/28 02:19:44 aharvey Exp $
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>
44  * @copyright  1997-2007 The PHP Group
45  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
46  * @version    Release: 1.7.14RC1
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 cannot 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     /**
151      * This constructor calls <kbd>$this->DB_common()</kbd>
152      *
153      * @return void
154      */
155     function DB_pgsql()
156     {
157         $this->DB_common();
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      * 
196      * $db = DB::connect($dsn, $options);
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 {
280             @ini_set('track_errors', 1);
281             $this->connection = @call_user_func_array($connect_function,
282                                                       $params);
283             @ini_set('track_errors', $ini);
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     {
323         $ismanip = $this->_checkManip($query);
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         }
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
352          */
353         if ($ismanip) {
354             $this->affected = @pg_affected_rows($result);
355             return DB_OK;
356         } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|FETCH|SHOW)\s/si',
357                              $query))
358         {
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 cannot 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 cannot 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     // }}}
469     // {{{ quote()
470
471     /**
472      * @deprecated  Deprecated in release 1.6.0
473      * @internal
474      */
475     function quote($str)
476     {
477         return $this->quoteSmart($str);
478     }
479
480     // }}}
481     // {{{ quoteBoolean()
482
483     /**
484      * Formats a boolean value for use within a query in a locale-independent
485      * manner.
486      *
487      * @param boolean the boolean value to be quoted.
488      * @return string the quoted string.
489      * @see DB_common::quoteSmart()
490      * @since Method available since release 1.7.8.
491      */
492     function quoteBoolean($boolean) {
493         return $boolean ? 'TRUE' : 'FALSE';
494     }
495      
496     // }}}
497     // {{{ escapeSimple()
498
499     /**
500      * Escapes a string according to the current DBMS's standards
501      *
502      * {@internal PostgreSQL treats a backslash as an escape character,
503      * so they are escaped as well.
504      *
505      * @param string $str  the string to be escaped
506      *
507      * @return string  the escaped string
508      *
509      * @see DB_common::quoteSmart()
510      * @since Method available since Release 1.6.0
511      */
512     function escapeSimple($str)
513     {
514         if (function_exists('pg_escape_string')) {
515             /* This fixes an undocumented BC break in PHP 5.2.0 which changed
516              * the prototype of pg_escape_string. I'm not thrilled about having
517              * to sniff the PHP version, quite frankly, but it's the only way
518              * to deal with the problem. Revision 1.331.2.13.2.10 on
519              * php-src/ext/pgsql/pgsql.c (PHP_5_2 branch) is to blame, for the
520              * record. */
521             if (version_compare(PHP_VERSION, '5.2.0', '>=')) {
522                 return pg_escape_string($this->connection, $str);
523             } else {
524                 return pg_escape_string($str);
525             }
526         } else {
527             return str_replace("'", "''", str_replace('\\', '\\\\', $str));
528         }
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 cannot 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 cannot 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 (!$native) {
784             $native = 'Database connection has been lost.';
785             $errno = DB_ERROR_CONNECT_FAILED;
786         }
787         if ($errno === null) {
788             $errno = $this->errorCode($native);
789         }
790         return $this->raiseError($errno, null, null, null, $native);
791     }
792
793     // }}}
794     // {{{ errorNative()
795
796     /**
797      * Gets the DBMS' native error message produced by the last query
798      *
799      * {@internal Error messages are used instead of error codes 
800      * in order to support older versions of PostgreSQL.}}
801      *
802      * @return string  the DBMS' error message
803      */
804     function errorNative()
805     {
806         return @pg_errormessage($this->connection);
807     }
808
809     // }}}
810     // {{{ errorCode()
811
812     /**
813      * Determines PEAR::DB error code from the database's text error message.
814      *
815      * @param  string  $errormsg  error message returned from the database
816      * @return integer  an error number from a DB error constant
817      */
818     function errorCode($errormsg)
819     {
820         static $error_regexps;
821         if (!isset($error_regexps)) {
822             $error_regexps = array(
823                 '/column .* (of relation .*)?does not exist/i'
824                     => DB_ERROR_NOSUCHFIELD,
825                 '/(relation|sequence|table).*does not exist|class .* not found/i'
826                     => DB_ERROR_NOSUCHTABLE,
827                 '/index .* does not exist/'
828                     => DB_ERROR_NOT_FOUND,
829                 '/relation .* already exists/i'
830                     => DB_ERROR_ALREADY_EXISTS,
831                 '/(divide|division) by zero$/i'
832                     => DB_ERROR_DIVZERO,
833                 '/pg_atoi: error in .*: can\'t parse /i'
834                     => DB_ERROR_INVALID_NUMBER,
835                 '/invalid input syntax for( type)? (integer|numeric)/i'
836                     => DB_ERROR_INVALID_NUMBER,
837                 '/value .* is out of range for type \w*int/i'
838                     => DB_ERROR_INVALID_NUMBER,
839                 '/integer out of range/i'
840                     => DB_ERROR_INVALID_NUMBER,
841                 '/value too long for type character/i'
842                     => DB_ERROR_INVALID,
843                 '/attribute .* not found|relation .* does not have attribute/i'
844                     => DB_ERROR_NOSUCHFIELD,
845                 '/column .* specified in USING clause does not exist in (left|right) table/i'
846                     => DB_ERROR_NOSUCHFIELD,
847                 '/parser: parse error at or near/i'
848                     => DB_ERROR_SYNTAX,
849                 '/syntax error at/'
850                     => DB_ERROR_SYNTAX,
851                 '/column reference .* is ambiguous/i'
852                     => DB_ERROR_SYNTAX,
853                 '/permission denied/'
854                     => DB_ERROR_ACCESS_VIOLATION,
855                 '/violates not-null constraint/'
856                     => DB_ERROR_CONSTRAINT_NOT_NULL,
857                 '/violates [\w ]+ constraint/'
858                     => DB_ERROR_CONSTRAINT,
859                 '/referential integrity violation/'
860                     => DB_ERROR_CONSTRAINT,
861                 '/more expressions than target columns/i'
862                     => DB_ERROR_VALUE_COUNT_ON_ROW,
863             );
864         }
865         foreach ($error_regexps as $regexp => $code) {
866             if (preg_match($regexp, $errormsg)) {
867                 return $code;
868             }
869         }
870         // Fall back to DB_ERROR if there was no mapping.
871         return DB_ERROR;
872     }
873
874     // }}}
875     // {{{ tableInfo()
876
877     /**
878      * Returns information about a table or a result set
879      *
880      * NOTE: only supports 'table' and 'flags' if <var>$result</var>
881      * is a table name.
882      *
883      * @param object|string  $result  DB_result object from a query or a
884      *                                 string containing the name of a table.
885      *                                 While this also accepts a query result
886      *                                 resource identifier, this behavior is
887      *                                 deprecated.
888      * @param int            $mode    a valid tableInfo mode
889      *
890      * @return array  an associative array with the information requested.
891      *                 A DB_Error object on failure.
892      *
893      * @see DB_common::tableInfo()
894      */
895     function tableInfo($result, $mode = null)
896     {
897         if (is_string($result)) {
898             /*
899              * Probably received a table name.
900              * Create a result resource identifier.
901              */
902             $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
903             $got_string = true;
904         } elseif (isset($result->result)) {
905             /*
906              * Probably received a result object.
907              * Extract the result resource identifier.
908              */
909             $id = $result->result;
910             $got_string = false;
911         } else {
912             /*
913              * Probably received a result resource identifier.
914              * Copy it.
915              * Deprecated.  Here for compatibility only.
916              */
917             $id = $result;
918             $got_string = false;
919         }
920
921         if (!is_resource($id)) {
922             return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
923         }
924
925         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
926             $case_func = 'strtolower';
927         } else {
928             $case_func = 'strval';
929         }
930
931         $count = @pg_numfields($id);
932         $res   = array();
933
934         if ($mode) {
935             $res['num_fields'] = $count;
936         }
937
938         for ($i = 0; $i < $count; $i++) {
939             $res[$i] = array(
940                 'table' => $got_string ? $case_func($result) : '',
941                 'name'  => $case_func(@pg_fieldname($id, $i)),
942                 'type'  => @pg_fieldtype($id, $i),
943                 'len'   => @pg_fieldsize($id, $i),
944                 'flags' => $got_string
945                            ? $this->_pgFieldFlags($id, $i, $result)
946                            : '',
947             );
948             if ($mode & DB_TABLEINFO_ORDER) {
949                 $res['order'][$res[$i]['name']] = $i;
950             }
951             if ($mode & DB_TABLEINFO_ORDERTABLE) {
952                 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
953             }
954         }
955
956         // free the result only if we were called on a table
957         if ($got_string) {
958             @pg_freeresult($id);
959         }
960         return $res;
961     }
962
963     // }}}
964     // {{{ _pgFieldFlags()
965
966     /**
967      * Get a column's flags
968      *
969      * Supports "not_null", "default_value", "primary_key", "unique_key"
970      * and "multiple_key".  The default value is passed through
971      * rawurlencode() in case there are spaces in it.
972      *
973      * @param int $resource   the PostgreSQL result identifier
974      * @param int $num_field  the field number
975      *
976      * @return string  the flags
977      *
978      * @access private
979      */
980     function _pgFieldFlags($resource, $num_field, $table_name)
981     {
982         $field_name = @pg_fieldname($resource, $num_field);
983
984         // Check if there's a schema in $table_name and update things
985         // accordingly.
986         $from = 'pg_attribute f, pg_class tab, pg_type typ';
987         if (strpos($table_name, '.') !== false) {
988             $from .= ', pg_namespace nsp';
989             list($schema, $table) = explode('.', $table_name);
990             $tableWhere = "tab.relname = '$table' AND tab.relnamespace = nsp.oid AND nsp.nspname = '$schema'";
991         } else {
992             $tableWhere = "tab.relname = '$table_name'";
993         }
994
995         $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
996                                 FROM $from
997                                 WHERE tab.relname = typ.typname
998                                 AND typ.typrelid = f.attrelid
999                                 AND f.attname = '$field_name'
1000                                 AND $tableWhere");
1001         if (@pg_numrows($result) > 0) {
1002             $row = @pg_fetch_row($result, 0);
1003             $flags  = ($row[0] == 't') ? 'not_null ' : '';
1004
1005             if ($row[1] == 't') {
1006                 $result = @pg_exec($this->connection, "SELECT a.adsrc
1007                                     FROM $from, pg_attrdef a
1008                                     WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
1009                                     AND f.attrelid = a.adrelid AND f.attname = '$field_name'
1010                                     AND $tableWhere AND f.attnum = a.adnum");
1011                 $row = @pg_fetch_row($result, 0);
1012                 $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]);
1013                 $flags .= 'default_' . rawurlencode($num) . ' ';
1014             }
1015         } else {
1016             $flags = '';
1017         }
1018         $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
1019                                 FROM $from, pg_index i
1020                                 WHERE tab.relname = typ.typname
1021                                 AND typ.typrelid = f.attrelid
1022                                 AND f.attrelid = i.indrelid
1023                                 AND f.attname = '$field_name'
1024                                 AND $tableWhere");
1025         $count = @pg_numrows($result);
1026
1027         for ($i = 0; $i < $count ; $i++) {
1028             $row = @pg_fetch_row($result, $i);
1029             $keys = explode(' ', $row[2]);
1030
1031             if (in_array($num_field + 1, $keys)) {
1032                 $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : '';
1033                 $flags .= ($row[1] == 't') ? 'primary_key ' : '';
1034                 if (count($keys) > 1)
1035                     $flags .= 'multiple_key ';
1036             }
1037         }
1038
1039         return trim($flags);
1040     }
1041
1042     // }}}
1043     // {{{ getSpecialQuery()
1044
1045     /**
1046      * Obtains the query string needed for listing a given type of objects
1047      *
1048      * @param string $type  the kind of objects you want to retrieve
1049      *
1050      * @return string  the SQL query string or null if the driver doesn't
1051      *                  support the object type requested
1052      *
1053      * @access protected
1054      * @see DB_common::getListOf()
1055      */
1056     function getSpecialQuery($type)
1057     {
1058         switch ($type) {
1059             case 'tables':
1060                 return 'SELECT c.relname AS "Name"'
1061                         . ' FROM pg_class c, pg_user u'
1062                         . ' WHERE c.relowner = u.usesysid'
1063                         . " AND c.relkind = 'r'"
1064                         . ' AND NOT EXISTS'
1065                         . ' (SELECT 1 FROM pg_views'
1066                         . '  WHERE viewname = c.relname)'
1067                         . " AND c.relname !~ '^(pg_|sql_)'"
1068                         . ' UNION'
1069                         . ' SELECT c.relname AS "Name"'
1070                         . ' FROM pg_class c'
1071                         . " WHERE c.relkind = 'r'"
1072                         . ' AND NOT EXISTS'
1073                         . ' (SELECT 1 FROM pg_views'
1074                         . '  WHERE viewname = c.relname)'
1075                         . ' AND NOT EXISTS'
1076                         . ' (SELECT 1 FROM pg_user'
1077                         . '  WHERE usesysid = c.relowner)'
1078                         . " AND c.relname !~ '^pg_'";
1079             case 'schema.tables':
1080                 return "SELECT schemaname || '.' || tablename"
1081                         . ' AS "Name"'
1082                         . ' FROM pg_catalog.pg_tables'
1083                         . ' WHERE schemaname NOT IN'
1084                         . " ('pg_catalog', 'information_schema', 'pg_toast')";
1085             case 'schema.views':
1086                 return "SELECT schemaname || '.' || viewname from pg_views WHERE schemaname"
1087                         . " NOT IN ('information_schema', 'pg_catalog')";
1088             case 'views':
1089                 // Table cols: viewname | viewowner | definition
1090                 return 'SELECT viewname from pg_views WHERE schemaname'
1091                         . " NOT IN ('information_schema', 'pg_catalog')";
1092             case 'users':
1093                 // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd  |valuntil
1094                 return 'SELECT usename FROM pg_user';
1095             case 'databases':
1096                 return 'SELECT datname FROM pg_database';
1097             case 'functions':
1098             case 'procedures':
1099                 return 'SELECT proname FROM pg_proc WHERE proowner <> 1';
1100             default:
1101                 return null;
1102         }
1103     }
1104
1105     // }}}
1106     // {{{ _checkManip()
1107
1108     /**
1109      * Checks if the given query is a manipulation query. This also takes into
1110      * account the _next_query_manip flag and sets the _last_query_manip flag
1111      * (and resets _next_query_manip) according to the result.
1112      *
1113      * @param string The query to check.
1114      *
1115      * @return boolean true if the query is a manipulation query, false
1116      * otherwise
1117      *
1118      * @access protected
1119      */
1120     function _checkManip($query)
1121     {
1122         return (preg_match('/^\s*(SAVEPOINT|RELEASE)\s+/i', $query)
1123                 || parent::_checkManip($query));
1124     }
1125
1126 }
1127
1128 /*
1129  * Local variables:
1130  * tab-width: 4
1131  * c-basic-offset: 4
1132  * End:
1133  */
1134
1135 ?>