Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// +----------------------------------------------------------------------+
3
// | PHP Version 4                                                        |
4
// +----------------------------------------------------------------------+
5
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
6
// | Stig. S. Bakken, Lukas Smith                                         |
7
// | All rights reserved.                                                 |
8
// +----------------------------------------------------------------------+
9
// | MDB is a merge of PEAR DB and Metabases that provides a unified DB   |
10
// | API as well as database abstraction for PHP applications.            |
11
// | This LICENSE is in the BSD license style.                            |
12
// |                                                                      |
13
// | Redistribution and use in source and binary forms, with or without   |
14
// | modification, are permitted provided that the following conditions   |
15
// | are met:                                                             |
16
// |                                                                      |
17
// | Redistributions of source code must retain the above copyright       |
18
// | notice, this list of conditions and the following disclaimer.        |
19
// |                                                                      |
20
// | Redistributions in binary form must reproduce the above copyright    |
21
// | notice, this list of conditions and the following disclaimer in the  |
22
// | documentation and/or other materials provided with the distribution. |
23
// |                                                                      |
24
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
25
// | Lukas Smith nor the names of his contributors may be used to endorse |
26
// | or promote products derived from this software without specific prior|
27
// | written permission.                                                  |
28
// |                                                                      |
29
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
30
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
31
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
32
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
33
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
34
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
35
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
36
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
37
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
38
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
39
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
40
// | POSSIBILITY OF SUCH DAMAGE.                                          |
41
// +----------------------------------------------------------------------+
42
// | Author: Paul Cooper <pgc@ucecom.com>                                 |
43
// +----------------------------------------------------------------------+
44
//
45
// $Id: pgsql.php,v 1.62.4.20 2004/03/12 16:19:30 lsmith Exp $
46
 
47
require_once('MDB/Common.php');
48
 
49
/**
50
 * MDB PostGreSQL driver
51
 *
52
 * Notes:
53
 * - Creation of new databases is based on database template1.
54
 *
55
 * - The decimal type fields are emulated with integer fields.
56
 *
57
 * - PostgreSQL stores large objects in files managed by the server.
58
 *   Tables with large object fields only store identifiers pointing to those
59
 *   files. If you delete or update rows of those tables, the actual large
60
 *   object files are not deleted from the server file system. Therefore you may
61
 *   need to reclaim large object field space by deleting those files manually.
62
 *
63
 * @package MDB
64
 * @category Database
65
 * @author  Paul Cooper <pgc@ucecom.com>
66
 */
67
 
68
class MDB_pgsql extends MDB_Common
69
{
70
    var $connection = 0;
71
    var $connected_host;
72
    var $connected_port;
73
    var $selected_database = '';
74
    var $opened_persistent = '';
75
 
76
    var $escape_quotes = "\\";
77
    var $decimal_factor = 1.0;
78
 
79
    var $highest_fetched_row = array();
80
    var $columns = array();
81
 
82
    // }}}
83
    // {{{ constructor
84
 
85
    /**
86
    * Constructor
87
    */
88
    function MDB_pgsql()
89
    {
90
        $this->MDB_Common();
91
        $this->phptype = 'pgsql';
92
        $this->dbsyntax = 'pgsql';
93
 
94
        $this->supported['Sequences'] = 1;
95
        $this->supported['Indexes'] = 1;
96
        $this->supported['SummaryFunctions'] = 1;
97
        $this->supported['OrderByText'] = 1;
98
        $this->supported['Transactions'] = 1;
99
        $this->supported['CurrId'] = 1;
100
        $this->supported['SelectRowRanges'] = 1;
101
        $this->supported['LOBs'] = 1;
102
        $this->supported['Replace'] = 1;
103
        $this->supported['SubSelects'] = 1;
104
 
105
        $this->decimal_factor = pow(10.0, $this->decimal_places);
106
    }
107
 
108
    // }}}
109
    // {{{ errorCode()
110
 
111
    /**
112
     * Map native error codes to DB's portable ones.  Requires that
113
     * the DB implementation's constructor fills in the $errorcode_map
114
     * property.
115
     *
116
     * @param $nativecode the native error code, as returned by the backend
117
     * database extension (string or integer)
118
     * @return int a portable MDB error code, or FALSE if this DB
119
     * implementation has no mapping for the given error code.
120
     */
121
    function errorCode($errormsg)
122
    {
123
        static $error_regexps;
124
        if (empty($error_regexps)) {
125
            $error_regexps = array(
126
                '/([Tt]able does not exist\.|[Rr]elation [\"\'].*[\"\'] does not exist|[Ss]equence does not exist|[Cc]lass ".+" not found)$/' => MDB_ERROR_NOSUCHTABLE,
127
                '/[Tt]able [\"\'].*[\"\'] does not exist/' => MDB_ERROR_NOSUCHTABLE,
128
                '/[Rr]elation [\"\'].*[\"\'] already exists|[Cc]annot insert a duplicate key into (a )?unique index.*/' => MDB_ERROR_ALREADY_EXISTS,
129
                '/divide by zero$/'                     => MDB_ERROR_DIVZERO,
130
                '/pg_atoi: error in .*: can\'t parse /' => MDB_ERROR_INVALID_NUMBER,
131
                '/ttribute [\"\'].*[\"\'] not found$|[Rr]elation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => MDB_ERROR_NOSUCHFIELD,
132
                '/parser: parse error at or near \"/'   => MDB_ERROR_SYNTAX,
133
                '/syntax error at/'                     => MDB_ERROR_SYNTAX,
134
                '/violates not-null constraint/'        => MDB_ERROR_CONSTRAINT_NOT_NULL,
135
                '/violates [\w ]+ constraint/'          => MDB_ERROR_CONSTRAINT,
136
                '/referential integrity violation/'     => MDB_ERROR_CONSTRAINT,
137
                '/deadlock detected/'                   => MDB_ERROR_DEADLOCK
138
            );
139
        }
140
        foreach ($error_regexps as $regexp => $code) {
141
            if (preg_match($regexp, $errormsg)) {
142
                return($code);
143
            }
144
        }
145
        // Fall back to MDB_ERROR if there was no mapping.
146
        return(MDB_ERROR);
147
    }
148
 
149
    // }}}
150
    // {{{ pgsqlRaiseError()
151
 
152
    /**
153
     * This method is used to communicate an error and invoke error
154
     * callbacks etc.  Basically a wrapper for MDB::raiseError
155
     * that checks for native error msgs.
156
     *
157
     * @param integer $errno error code
158
     * @param string  $message userinfo message
159
     * @return object a PEAR error object
160
     * @access public
161
     * @see PEAR_Error
162
     */
163
    function pgsqlRaiseError($errno = NULL, $message = NULL)
164
    {
165
        if ($this->connection) {
166
            $error = @pg_errormessage($this->connection);
167
        } else {
168
            $error = @pg_errormessage();
169
        }
170
        return($this->raiseError($this->errorCode($error), NULL, NULL, $message, $error));
171
    }
172
 
173
    // }}}
174
    // {{{ errorNative()
175
 
176
    /**
177
     * Get the native error code of the last error (if any) that
178
     * occured on the current connection.
179
     *
180
     * @access public
181
     *
182
     * @return int native pgsql error code
183
     */
184
    function errorNative()
185
    {
186
        return @pg_errormessage($this->connection);
187
    }
188
 
189
 
190
    // }}}
191
    // {{{ autoCommit()
192
 
193
    /**
194
     * Define whether database changes done on the database be automatically
195
     * committed. This function may also implicitly start or end a transaction.
196
     *
197
     * @param boolean $auto_commit flag that indicates whether the database
198
     *     changes should be committed right after executing every query
199
     *     statement. If this argument is 0 a transaction implicitly started.
200
     *     Otherwise, if a transaction is in progress it is ended by committing
201
     *     any database changes that were pending.
202
     * @return mixed MDB_OK on success, a MDB error on failure
203
     * @access public
204
     */
205
    function autoCommit($auto_commit)
206
    {
207
        $this->debug('AutoCommit: '.($auto_commit ? 'On' : 'Off'));
208
        if ($this->auto_commit == $auto_commit) {
209
            return(MDB_OK);
210
        }
211
        if ($this->connection) {
212
            if (MDB::isError($result = $this->_doQuery($auto_commit ? 'END' : 'BEGIN')))
213
                return($result);
214
        }
215
        $this->auto_commit = $auto_commit;
216
        $this->in_transaction = !$auto_commit;
217
        return(MDB_OK);
218
    }
219
 
220
    // }}}
221
    // {{{ commit()
222
 
223
    /**
224
     * Commit the database changes done during a transaction that is in
225
     * progress. This function may only be called when auto-committing is
226
     * disabled, otherwise it will fail. Therefore, a new transaction is
227
     * implicitly started after committing the pending changes.
228
     *
229
     * @return mixed MDB_OK on success, a MDB error on failure
230
     * @access public
231
     */
232
    function commit()
233
    {
234
         $this->debug('Commit Transaction');
235
        if ($this->auto_commit) {
236
            return($this->raiseError(MDB_ERROR, NULL, NULL, 'Commit: transaction changes are being auto commited'));
237
        }
238
        return($this->_doQuery('COMMIT') && $this->_doQuery('BEGIN'));
239
    }
240
 
241
    // }}}
242
    // {{{ rollback()
243
 
244
    /**
245
     * Cancel any database changes done during a transaction that is in
246
     * progress. This function may only be called when auto-committing is
247
     * disabled, otherwise it will fail. Therefore, a new transaction is
248
     * implicitly started after canceling the pending changes.
249
     *
250
     * @return mixed MDB_OK on success, a MDB error on failure
251
     * @access public
252
     */
253
    function rollback()
254
    {
255
         $this->debug('Rollback Transaction');
256
        if ($this->auto_commit) {
257
            return($this->raiseError(MDB_ERROR, NULL, NULL, 'Rollback: transactions can not be rolled back when changes are auto commited'));
258
        }
259
        return($this->_doQuery('ROLLBACK') && $this->_doQuery('BEGIN'));
260
    }
261
 
262
    // }}}
263
    // {{{ _doConnect()
264
 
265
    /**
266
     * Does the grunt work of connecting to the database
267
     *
268
     * @return mixed connection resource on success, MDB_Error on failure
269
     * @access private
270
     **/
271
    function _doConnect($database_name, $persistent)
272
    {
273
        $function = ($persistent ? 'pg_pconnect' : 'pg_connect');
274
        if (!function_exists($function)) {
275
            return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 'doConnect: PostgreSQL support is not available in this PHP configuration'));
276
        }
277
        $port = (isset($this->port) ? $this->port : '');
278
        if ($database_name == '') {
279
            $database_name = 'template1';
280
        }
281
        $connect_string = 'dbname='.$database_name;
282
        if ($this->host != '') {
283
            $connect_string .= ' host='.$this->host;
284
        }
285
        if ($port != '') {
286
            $connect_string .= ' port='.strval($port);
287
        }
288
        if ($this->user != '') {
289
            $connect_string .= ' user='.$this->user;
290
        }
291
        if ($this->password != '') {
292
            $connect_string .= ' password='.$this->password;
293
        }
294
        putenv('PGDATESTYLE=ISO');
295
        if (($connection = @$function($connect_string)) > 0) {
296
            return($connection);
297
        }
298
        if (isset($php_errormsg)) {
299
            $error_msg = $php_errormsg;
300
        } else {
301
            $error_msg = 'Could not connect to PostgreSQL server';
302
        }
303
        return($this->raiseError(MDB_ERROR_CONNECT_FAILED, NULL, NULL, 'doConnect: '.$error_msg));
304
    }
305
 
306
    // }}}
307
    // {{{ connect()
308
 
309
    /**
310
     * Connect to the database
311
     *
312
     * @return TRUE on success, MDB_Error on failure
313
     * @access public
314
     **/
315
    function connect()
316
    {
317
        $port = (isset($this->options['port']) ? $this->options['port'] : '');
318
        if($this->connection != 0) {
319
            if (!strcmp($this->connected_host, $this->host)
320
                && !strcmp($this->connected_port, $port)
321
                && !strcmp($this->selected_database, $this->database_name)
322
                && ($this->opened_persistent == $this->options['persistent']))
323
            {
324
                return(MDB_OK);
325
            }
326
            @pg_close($this->connection);
327
            $this->affected_rows = -1;
328
            $this->connection = 0;
329
        }
330
 
331
        if(!PEAR::loadExtension($this->phptype)) {
332
            return(PEAR::raiseError(NULL, MDB_ERROR_NOT_FOUND,
333
                NULL, NULL, 'extension '.$this->phptype.' is not compiled into PHP',
334
                'MDB_Error', TRUE));
335
        }
336
 
337
        if(function_exists('pg_cmdTuples')) {
338
            $connection = $this->_doConnect('template1', 0);
339
            if (!MDB::isError($connection)) {
340
                if (($result = @pg_exec($connection, 'BEGIN'))) {
341
                    $error_reporting = error_reporting(63);
342
                    @pg_cmdtuples($result);
343
                    if (!isset($php_errormsg) || strcmp($php_errormsg, 'This compilation does not support pg_cmdtuples()')) {
344
                        $this->supported['AffectedRows'] = 1;
345
                    }
346
                    error_reporting($error_reporting);
347
                } else {
348
                    $err = $this->raiseError(MDB_ERROR, NULL, NULL, 'Setup: '.@pg_errormessage($connection));
349
                }
350
                @pg_close($connection);
351
            } else {
352
                $err = $this->raiseError(MDB_ERROR, NULL, NULL, 'Setup: could not execute BEGIN');
353
            }
354
            if (isset($err) && MDB::isError($err)) {
355
                return($err);
356
            }
357
        }
358
        $connection = $this->_doConnect($this->database_name, $this->options['persistent']);
359
        if (MDB::isError($connection)) {
360
            return($connection);
361
        }
362
        $this->connection = $connection;
363
 
364
        if (!$this->auto_commit && MDB::isError($trans_result = $this->_doQuery('BEGIN'))) {
365
            pg_Close($this->connection);
366
            $this->connection = 0;
367
            $this->affected_rows = -1;
368
            return($trans_result);
369
        }
370
        $this->connected_host = $this->host;
371
        $this->connected_port = $port;
372
        $this->selected_database = $this->database_name;
373
        $this->opened_persistent = $this->options['persistent'];
374
        return(MDB_OK);
375
    }
376
 
377
    // }}}
378
    // {{{ _close()
379
    /**
380
     * Close the database connection
381
     *
382
     * @return boolean
383
     * @access private
384
     **/
385
    function _close()
386
    {
387
        if ($this->connection != 0) {
388
            if (!$this->auto_commit) {
389
                $this->_doQuery('END');
390
            }
391
            @pg_close($this->connection);
392
            $this->connection = 0;
393
            $this->affected_rows = -1;
394
 
395
            unset($GLOBALS['_MDB_databases'][$this->database]);
396
            return(MDB_OK);
397
        }
398
        return(MDB_ERROR);
399
    }
400
 
401
    // }}}
402
    // {{{ _doQuery()
403
 
404
    /**
405
     * Execute a query
406
     * @param string $query the SQL query
407
     * @return mixed result identifier if query executed, else MDB_error
408
     * @access private
409
     **/
410
    function _doQuery($query)
411
    {
412
        if (($result = @pg_Exec($this->connection, $query))) {
413
            $this->affected_rows = (isset($this->supported['AffectedRows']) ? @pg_cmdtuples($result) : -1);
414
        } else {
415
            $error = @pg_errormessage($this->connection);
416
            return($this->pgsqlRaiseError());
417
        }
418
        return($result);
419
    }
420
 
421
    // }}}
422
    // {{{ _standaloneQuery()
423
 
424
    /**
425
     * execute a query
426
     *
427
     * @param string $query
428
     * @return
429
     * @access private
430
     */
431
    function _standaloneQuery($query)
432
    {
433
        if (($connection = $this->_doConnect('template1', 0)) == 0) {
434
            return($this->raiseError(MDB_ERROR_CONNECT_FAILED, NULL, NULL, '_standaloneQuery: Cannot connect to template1'));
435
        }
436
        if (!($result = @pg_Exec($connection, $query))) {
437
            $this->raiseError(MDB_ERROR, NULL, NULL, '_standaloneQuery: ' . @pg_errormessage($connection));
438
        }
439
        pg_Close($connection);
440
        return($result);
441
    }
442
 
443
    // }}}
444
    // {{{ query()
445
 
446
    /**
447
     * Send a query to the database and return any results
448
     *
449
     * @param string $query the SQL query
450
     * @param array $types array that contains the types of the columns in
451
     *                         the result set
452
     * @return mixed result identifier if query executed, else MDB_error
453
     * @access public
454
     **/
455
    function query($query, $types = NULL)
456
    {
457
        $this->debug("Query: $query");
458
        $ismanip = MDB::isManip($query);
459
        $this->last_query = $query;
460
        $first = $this->first_selected_row;
461
        $limit = $this->selected_row_limit;
462
        $this->first_selected_row = $this->selected_row_limit = 0;
463
        $connected = $this->connect();
464
        if (MDB::isError($connected)) {
465
            return($connected);
466
        }
467
 
468
        if (!$ismanip && $limit > 0) {
469
             if ($this->auto_commit && MDB::isError($this->_doQuery('BEGIN'))) {
470
                 return($this->raiseError(MDB_ERROR));
471
             }
472
             $result = $this->_doQuery('DECLARE select_cursor SCROLL CURSOR FOR '.$query);
473
             if (!MDB::isError($result)) {
474
                 if ($first > 0 && MDB::isError($result = $this->_doQuery("MOVE FORWARD $first FROM select_cursor"))) {
475
                     $this->freeResult($result);
476
                     return($result);
477
                 }
478
                 if (MDB::isError($result = $this->_doQuery("FETCH FORWARD $limit FROM select_cursor"))) {
479
                     $this->freeResult($result);
480
                     return($result);
481
                 }
482
             } else {
483
                 return($result);
484
             }
485
             if ($this->auto_commit && MDB::isError($result2 = $this->_doQuery('END'))) {
486
                 $this->freeResult($result);
487
                 return($result2);
488
             }
489
         } else {
490
            $result = $this->_doQuery($query);
491
            if (MDB::isError($result)) {
492
                return($result);
493
            }
494
        }
495
        if ($ismanip) {
496
            $this->affected_rows = @pg_cmdtuples($result);
497
            return(MDB_OK);
498
        } elseif ((preg_match('/^\s*\(?\s*SELECT\s+/si', $query)
499
                && !preg_match('/^\s*\(?\s*SELECT\s+INTO\s/si', $query)
500
            ) || preg_match('/^\s*EXPLAIN/si',$query )
501
        ) {
502
            /* PostgreSQL commands:
503
               ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
504
               CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
505
               GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
506
               REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
507
               UNLISTEN, UPDATE, VACUUM
508
            */
509
            $result_value = intval($result);
510
            $this->highest_fetched_row[$result_value] = -1;
511
            if ($types != NULL) {
512
                if (!is_array($types)) {
513
                    $types = array($types);
514
                }
515
                if (MDB::isError($err = $this->setResultTypes($result, $types))) {
516
                    $this->freeResult($result);
517
                    return($err);
518
                }
519
            }
520
            return($result);
521
        } else {
522
            $this->affected_rows = 0;
523
            return(MDB_OK);
524
        }
525
        return($this->raiseError(MDB_ERROR));
526
    }
527
 
528
    // }}}
529
    // {{{ getColumnNames()
530
 
531
    /**
532
     * Retrieve the names of columns returned by the DBMS in a query result.
533
     *
534
     * @param resource $result  result identifier
535
     * @return mixed associative array variable
536
     *      that holds the names of columns. The indexes of the array are
537
     *      the column names mapped to lower case and the values are the
538
     *      respective numbers of the columns starting from 0. Some DBMS may
539
     *      not return any columns when the result set does not contain any
540
     *      rows.
541
     *     a MDB error on failure
542
     * @access public
543
     */
544
    function getColumnNames($result)
545
    {
546
        $result_value = intval($result);
547
        if (!isset($this->highest_fetched_row[$result_value])) {
548
            return($this->raiseError(MDB_ERROR, NULL, NULL, 'Get Column Names: specified an nonexistant result set'));
549
        }
550
        if (!isset($this->columns[$result_value])) {
551
            $this->columns[$result_value] = array();
552
            $columns = @pg_numfields($result);
553
            for($column = 0; $column < $columns; $column++) {
554
                $field_name = @pg_fieldname($result, $column);
555
                if ($this->options['optimize'] == 'portability') {
556
                    $field_name = strtolower($field_name);
557
                }
558
                $this->columns[$result_value][$field_name] = $column;
559
            }
560
        }
561
        return($this->columns[$result_value]);
562
    }
563
 
564
    // }}}
565
    // {{{ numCols()
566
 
567
    /**
568
     * Count the number of columns returned by the DBMS in a query result.
569
     *
570
     * @param resource $result result identifier
571
     * @return mixed integer value with the number of columns, a MDB error
572
     *      on failure
573
     * @access public
574
     */
575
    function numCols($result)
576
    {
577
        $result_value = intval($result);
578
        if (!isset($this->highest_fetched_row[$result_value])) {
579
            return($this->raiseError(MDB_ERROR, NULL, NULL, 'numCols: specified an nonexistant result set'));
580
        }
581
        return(pg_numfields($result));
582
    }
583
 
584
    // }}}
585
    // {{{ endOfResult()
586
 
587
    /**
588
    * check if the end of the result set has been reached
589
    *
590
    * @param resource    $result result identifier
591
    * @return mixed TRUE or FALSE on sucess, a MDB error on failure
592
    * @access public
593
    */
594
    function endOfResult($result)
595
    {
596
        $result_value = intval($result);
597
        if (!isset($this->highest_fetched_row[$result_value])) {
598
            return($this->raiseError(MDB_ERROR, NULL, NULL, 'End of result attempted to check the end of an unknown result'));
599
        }
600
        return($this->highest_fetched_row[$result_value] >= $this->numRows($result) - 1);
601
    }
602
 
603
    // }}}
604
    // {{{ fetch()
605
 
606
    /**
607
     * fetch value from a result set
608
     *
609
     * @param resource $result result identifier
610
     * @param int $row number of the row where the data can be found
611
     * @param int $field field number where the data can be found
612
     * @return mixed string on success, a MDB error on failure
613
     * @access public
614
     */
615
    function fetch($result, $row, $field)
616
    {
617
        $result_value = intval($result);
618
        $this->highest_fetched_row[$result_value] = max($this->highest_fetched_row[$result_value], $row);
619
        $res = @pg_result($result, $row, $field);
620
        if ($res === FALSE && $res != NULL) {
621
            return($this->pgsqlRaiseError());
622
        }
623
        return($res);
624
    }
625
 
626
    // }}}
627
    // {{{ _retrieveLob()
628
 
629
    /**
630
     * fetch a float value from a result set
631
     *
632
     * @param int $lob handle to a lob created by the createLob() function
633
     * @return mixed MDB_OK on success, a MDB error on failure
634
     * @access private
635
     */
636
    function _retrieveLob($lob)
637
    {
638
        if (!isset($this->lobs[$lob])) {
639
            return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
640
                'Retrieve LOB: did not specified a valid lob'));
641
        }
642
        if (!isset($this->lobs[$lob]['Value'])) {
643
            if ($this->auto_commit) {
644
                if (!@pg_exec($this->connection, 'BEGIN')) {
645
                    return($this->raiseError(MDB_ERROR,  NULL, NULL,
646
                        'Retrieve LOB: ' . @pg_errormessage($this->connection)));
647
                }
648
                $this->lobs[$lob]['InTransaction'] = 1;
649
            }
650
            $this->lobs[$lob]['Value'] = $this->fetch($this->lobs[$lob]['Result'], $this->lobs[$lob]['Row'], $this->lobs[$lob]['Field']);
651
            if (!($this->lobs[$lob]['Handle'] = @pg_loopen($this->connection, $this->lobs[$lob]['Value'], 'r'))) {
652
                if (isset($this->lobs[$lob]['InTransaction'])) {
653
                    @pg_exec($this->connection, 'END');
654
                    unset($this->lobs[$lob]['InTransaction']);
655
                }
656
                unset($this->lobs[$lob]['Value']);
657
                return($this->raiseError(MDB_ERROR, NULL, NULL,
658
                    'Retrieve LOB: ' . @pg_errormessage($this->connection)));
659
            }
660
        }
661
        return(MDB_OK);
662
    }
663
 
664
    // }}}
665
    // {{{ endOfResultLob()
666
 
667
    /**
668
     * Determine whether it was reached the end of the large object and
669
     * therefore there is no more data to be read for the its input stream.
670
     *
671
     * @param int    $lob handle to a lob created by the createLob() function
672
     * @return mixed TRUE or FALSE on success, a MDB error on failure
673
     * @access public
674
     */
675
    function endOfResultLob($lob)
676
    {
677
        $lobresult = $this->_retrieveLob($lob);
678
        if (MDB::isError($lobresult)) {
679
            return($lobresult);
680
        }
681
        return(isset($this->lobs[$lob]['EndOfLOB']));
682
    }
683
 
684
    // }}}
685
    // {{{ _readResultLob()
686
 
687
    /**
688
     * Read data from large object input stream.
689
     *
690
     * @param int $lob handle to a lob created by the createLob() function
691
     * @param blob $data reference to a variable that will hold data to be
692
     *      read from the large object input stream
693
     * @param int $length integer value that indicates the largest ammount of
694
     *      data to be read from the large object input stream.
695
     * @return mixed length on success, a MDB error on failure
696
     * @access private
697
     */
698
    function _readResultLob($lob, &$data, $length)
699
    {
700
        $lobresult = $this->_retrieveLob($lob);
701
        if (MDB::isError($lobresult)) {
702
            return($lobresult);
703
        }
704
        $data = @pg_loread($this->lobs[$lob]['Handle'], $length);
705
        if (gettype($data) != 'string') {
706
            $this->raiseError(MDB_ERROR, NULL, NULL,
707
                'Read Result LOB: ' . @pg_errormessage($this->connection));
708
        }
709
        if (($length = strlen($data)) == 0) {
710
            $this->lobs[$lob]['EndOfLOB'] = 1;
711
        }
712
        return($length);
713
    }
714
 
715
    // }}}
716
    // {{{ _destroyResultLob()
717
 
718
    /**
719
     * Free any resources allocated during the lifetime of the large object
720
     * handler object.
721
     *
722
     * @param int $lob handle to a lob created by the createLob() function
723
     * @access private
724
     */
725
    function _destroyResultLob($lob)
726
    {
727
        if (isset($this->lobs[$lob])) {
728
            if (isset($this->lobs[$lob]['Value'])) {
729
                @pg_loclose($this->lobs[$lob]['Handle']);
730
                if (isset($this->lobs[$lob]['InTransaction'])) {
731
                    @pg_exec($this->connection, 'END');
732
                }
733
            }
734
            $this->lobs[$lob] = '';
735
        }
736
    }
737
 
738
    // }}}
739
    // {{{ fetchClob()
740
 
741
    /**
742
     * fetch a clob value from a result set
743
     *
744
     * @param resource $result result identifier
745
     * @param int $row number of the row where the data can be found
746
     * @param int $field field number where the data can be found
747
     * @return mixed content of the specified data cell, a MDB error on failure,
748
     *       a MDB error on failure
749
     * @access public
750
     */
751
    function fetchClob($result, $row, $field)
752
    {
753
        return($this->fetchLob($result, $row, $field));
754
    }
755
 
756
    // }}}
757
    // {{{ fetchBlob()
758
 
759
    /**
760
     * fetch a blob value from a result set
761
     *
762
     * @param resource $result result identifier
763
     * @param int $row number of the row where the data can be found
764
     * @param int $field field number where the data can be found
765
     * @return mixed content of the specified data cell, a MDB error on failure
766
     * @access public
767
     */
768
    function fetchBlob($result, $row, $field)
769
    {
770
        return($this->fetchLob($result, $row, $field));
771
    }
772
 
773
    // }}}
774
    // {{{ convertResult()
775
 
776
    /**
777
     * convert a value to a RDBMS indepdenant MDB type
778
     *
779
     * @param mixed $value value to be converted
780
     * @param int $type constant that specifies which type to convert to
781
     * @return mixed converted value or a MDB error on failure
782
     * @access public
783
     */
784
    function convertResult($value, $type)
785
    {
786
        switch ($type) {
787
            case MDB_TYPE_DECIMAL:
788
                return(sprintf('%.'.$this->decimal_places.'f',doubleval($value)/$this->decimal_factor));
789
            case MDB_TYPE_TIMESTAMP:
790
                return substr($value, 0, strlen('YYYY-MM-DD HH:MM:SS'));
791
            default:
792
                return($this->_baseConvertResult($value, $type));
793
        }
794
    }
795
 
796
    // }}}
797
    // {{{ resultIsNull()
798
 
799
    /**
800
     * Determine whether the value of a query result located in given row and
801
     *   field is a NULL.
802
     *
803
     * @param resource    $result result identifier
804
     * @param int    $row    number of the row where the data can be found
805
     * @param int    $field    field number where the data can be found
806
     * @return mixed TRUE or FALSE on success, a MDB error on failure
807
     * @access public
808
     */
809
    function resultIsNull($result, $row, $field)
810
    {
811
        $result_value = intval($result);
812
        $this->highest_fetched_row[$result_value] = max($this->highest_fetched_row[$result_value], $row);
813
        return(@pg_fieldisnull($result, $row, $field));
814
    }
815
 
816
    // }}}
817
    // {{{ numRows()
818
 
819
    /**
820
     * returns the number of rows in a result object
821
     *
822
     * @param ressource $result a valid result ressouce pointer
823
     * @return mixed MDB_Error or the number of rows
824
     * @access public
825
     */
826
    function numRows($result)
827
    {
828
        return(@pg_numrows($result));
829
    }
830
 
831
    // }}}
832
    // {{{ freeResult()
833
 
834
    /**
835
     * Free the internal resources associated with $result.
836
     *
837
     * @param $result result identifier
838
     * @return boolean TRUE on success, FALSE if $result is invalid
839
     * @access public
840
     */
841
    function freeResult($result)
842
    {
843
        $result_value = intval($result);
844
        if(isset($this->highest_fetched_row[$result_value])) {
845
            unset($this->highest_fetched_row[$result_value]);
846
        }
847
        if(isset($this->columns[$result_value])) {
848
            unset($this->columns[$result_value]);
849
        }
850
        if(isset($this->result_types[$result_value])) {
851
            unset($this->result_types[$result_value]);
852
        }
853
        return(@pg_freeresult($result));
854
    }
855
 
856
    // }}}
857
    // {{{ getTextDeclaration()
858
 
859
    /**
860
     * Obtain DBMS specific SQL code portion needed to declare an text type
861
     * field to be used in statements like CREATE TABLE.
862
     *
863
     * @param string $name   name the field to be declared.
864
     * @param string $field  associative array with the name of the properties
865
     *      of the field being declared as array indexes. Currently, the types
866
     *      of supported field properties are as follows:
867
     *
868
     *      length
869
     *          Integer value that determines the maximum length of the text
870
     *          field. If this argument is missing the field should be
871
     *          declared to have the longest length allowed by the DBMS.
872
     *
873
     *      default
874
     *          Text value to be used as default for this field.
875
     *
876
     *      notnull
877
     *          Boolean flag that indicates whether this field is constrained
878
     *          to not be set to NULL.
879
     * @return string  DBMS specific SQL code portion that should be used to
880
     *      declare the specified field.
881
     * @access public
882
     */
883
    function getTextDeclaration($name, $field)
884
    {
885
        return((isset($field['length']) ? "$name VARCHAR (" . $field['length'] . ')' : "$name TEXT") . (isset($field['default']) ? " DEFAULT '" . $field['default'] . "'" : '') . (isset($field['notnull']) ? ' NOT NULL' : ''));
886
    }
887
 
888
    // }}}
889
    // {{{ getClobDeclaration()
890
 
891
    /**
892
     * Obtain DBMS specific SQL code portion needed to declare an character
893
     * large object type field to be used in statements like CREATE TABLE.
894
     *
895
     * @param string $name   name the field to be declared.
896
     * @param string $field  associative array with the name of the properties
897
     *      of the field being declared as array indexes. Currently, the types
898
     *      of supported field properties are as follows:
899
     *
900
     *      length
901
     *          Integer value that determines the maximum length of the large
902
     *          object field. If this argument is missing the field should be
903
     *          declared to have the longest length allowed by the DBMS.
904
     *
905
     *      notnull
906
     *          Boolean flag that indicates whether this field is constrained
907
     *          to not be set to NULL.
908
     * @return string  DBMS specific SQL code portion that should be used to
909
     *      declare the specified field.
910
     * @access public
911
     */
912
    function getClobDeclaration($name, $field)
913
    {
914
        return("$name OID".(isset($field['notnull']) ? ' NOT NULL' : ''));
915
    }
916
 
917
    // }}}
918
    // {{{ getBlobDeclaration()
919
 
920
    /**
921
     * Obtain DBMS specific SQL code portion needed to declare an binary large
922
     * object type field to be used in statements like CREATE TABLE.
923
     *
924
     * @param string $name   name the field to be declared.
925
     * @param string $field  associative array with the name of the properties
926
     *      of the field being declared as array indexes. Currently, the types
927
     *      of supported field properties are as follows:
928
     *
929
     *      length
930
     *          Integer value that determines the maximum length of the large
931
     *          object field. If this argument is missing the field should be
932
     *          declared to have the longest length allowed by the DBMS.
933
     *
934
     *      notnull
935
     *          Boolean flag that indicates whether this field is constrained
936
     *          to not be set to NULL.
937
     * @return string  DBMS specific SQL code portion that should be used to
938
     *      declare the specified field.
939
     * @access public
940
     */
941
    function getBlobDeclaration($name, $field)
942
    {
943
        return("$name OID".(isset($field['notnull']) ? ' NOT NULL' : ''));
944
    }
945
 
946
    // }}}
947
    // {{{ getDateDeclaration()
948
 
949
    /**
950
     * Obtain DBMS specific SQL code portion needed to declare a date type
951
     * field to be used in statements like CREATE TABLE.
952
     *
953
     * @param string $name   name the field to be declared.
954
     * @param string $field  associative array with the name of the properties
955
     *      of the field being declared as array indexes. Currently, the types
956
     *      of supported field properties are as follows:
957
     *
958
     *      default
959
     *          Date value to be used as default for this field.
960
     *
961
     *      notnull
962
     *          Boolean flag that indicates whether this field is constrained
963
     *          to not be set to NULL.
964
     * @return string  DBMS specific SQL code portion that should be used to
965
     *      declare the specified field.
966
     * @access public
967
     */
968
    function getDateDeclaration($name, $field)
969
    {
970
        return($name.' DATE'.(isset($field['default']) ? ' DEFAULT \''.$field['default'] . "'" : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
971
    }
972
 
973
    // }}}
974
    // {{{ getTimeDeclaration()
975
 
976
    /**
977
     * Obtain DBMS specific SQL code portion needed to declare a time
978
     * field to be used in statements like CREATE TABLE.
979
     *
980
     * @param string $name   name the field to be declared.
981
     * @param string $field  associative array with the name of the properties
982
     *      of the field being declared as array indexes. Currently, the types
983
     *      of supported field properties are as follows:
984
     *
985
     *      default
986
     *          Time value to be used as default for this field.
987
     *
988
     *      notnull
989
     *          Boolean flag that indicates whether this field is constrained
990
     *          to not be set to NULL.
991
     * @return string  DBMS specific SQL code portion that should be used to
992
     *      declare the specified field.
993
     * @access public
994
     */
995
    function getTimeDeclaration($name, $field)
996
    {
997
        return($name.' TIME'.(isset($field['default']) ? ' DEFAULT \''.$field['default'].'\'' : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
998
    }
999
 
1000
    // }}}
1001
    // {{{ getFloatDeclaration()
1002
 
1003
    /**
1004
     * Obtain DBMS specific SQL code portion needed to declare a float type
1005
     * field to be used in statements like CREATE TABLE.
1006
     *
1007
     * @param string $name   name the field to be declared.
1008
     * @param string $field  associative array with the name of the properties
1009
     *      of the field being declared as array indexes. Currently, the types
1010
     *      of supported field properties are as follows:
1011
     *
1012
     *      default
1013
     *          Float value to be used as default for this field.
1014
     *
1015
     *      notnull
1016
     *          Boolean flag that indicates whether this field is constrained
1017
     *          to not be set to NULL.
1018
     * @return string  DBMS specific SQL code portion that should be used to
1019
     *      declare the specified field.
1020
     * @access public
1021
     */
1022
    function getFloatDeclaration($name, $field)
1023
    {
1024
        return("$name FLOAT8 ".(isset($field['default']) ? ' DEFAULT '.$this->getFloatValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
1025
    }
1026
 
1027
    // }}}
1028
    // {{{ getDecimalDeclaration()
1029
 
1030
    /**
1031
     * Obtain DBMS specific SQL code portion needed to declare a decimal type
1032
     * field to be used in statements like CREATE TABLE.
1033
     *
1034
     * @param string $name   name the field to be declared.
1035
     * @param string $field  associative array with the name of the properties
1036
     *      of the field being declared as array indexes. Currently, the types
1037
     *      of supported field properties are as follows:
1038
     *
1039
     *      default
1040
     *          Decimal value to be used as default for this field.
1041
     *
1042
     *      notnull
1043
     *          Boolean flag that indicates whether this field is constrained
1044
     *          to not be set to NULL.
1045
     * @return string  DBMS specific SQL code portion that should be used to
1046
     *      declare the specified field.
1047
     * @access public
1048
     */
1049
    function getDecimalDeclaration($name, $field)
1050
    {
1051
        return("$name INT8 ".(isset($field['default']) ? ' DEFAULT '.$this->getDecimalValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : ''));
1052
    }
1053
 
1054
    // }}}
1055
    // {{{ _getLobValue()
1056
 
1057
    /**
1058
     * Convert a text value into a DBMS specific format that is suitable to
1059
     * compose query statements.
1060
     *
1061
     * @param resource  $prepared_query query handle from prepare()
1062
     * @param           $parameter
1063
     * @param           $lob
1064
     * @return string text string that represents the given argument value in
1065
     *      a DBMS specific format.
1066
     * @access private
1067
     */
1068
    function _getLobValue($prepared_query, $parameter, $lob)
1069
    {
1070
        $connect = $this->connect();
1071
        if (MDB::isError($connect)) {
1072
            return($connect);
1073
        }
1074
        if ($this->auto_commit && !@pg_exec($this->connection, 'BEGIN')) {
1075
            return($this->raiseError(MDB_ERROR, NULL, NULL,
1076
                '_getLobValue: error starting transaction'));
1077
        }
1078
        if (($lo = @pg_locreate($this->connection))) {
1079
            if (($handle = @pg_loopen($this->connection, $lo, 'w'))) {
1080
                while (!$this->endOfLob($lob)) {
1081
                    $result = $this->readLob($lob, $data, $this->options['lob_buffer_length']);
1082
                    if (MDB::isError($result)) {
1083
                        break;
1084
                    }
1085
                    if (!@pg_lowrite($handle, $data)) {
1086
                        $result = $this->raiseError(MDB_ERROR, NULL, NULL,
1087
                            'Get LOB field value: ' . @pg_errormessage($this->connection));
1088
                        break;
1089
                    }
1090
                }
1091
                @pg_loclose($handle);
1092
                if (!MDB::isError($result)) {
1093
                    $value = strval($lo);
1094
                }
1095
            } else {
1096
                $result = $this->raiseError(MDB_ERROR, NULL, NULL,
1097
                    'Get LOB field value: ' . @pg_errormessage($this->connection));
1098
            }
1099
            if (MDB::isError($result)) {
1100
                $result = @pg_lounlink($this->connection, $lo);
1101
            }
1102
        } else {
1103
            $result = $this->raiseError(MDB_ERROR, NULL, NULL, 'Get LOB field value: ' . pg_ErrorMessage($this->connection));
1104
        }
1105
        if ($this->auto_commit) {
1106
            @pg_exec($this->connection, 'END');
1107
        }
1108
        if (MDB::isError($result)) {
1109
            return($result);
1110
        }
1111
        return($value);
1112
    }
1113
 
1114
    // }}}
1115
    // {{{ getClobValue()
1116
 
1117
    /**
1118
     * Convert a text value into a DBMS specific format that is suitable to
1119
     * compose query statements.
1120
     *
1121
     * @param resource  $prepared_query query handle from prepare()
1122
     * @param           $parameter
1123
     * @param           $clob
1124
     * @return string text string that represents the given argument value in
1125
     *      a DBMS specific format.
1126
     * @access public
1127
     */
1128
    function getClobValue($prepared_query, $parameter, $clob)
1129
    {
1130
        return($this->_getLobValue($prepared_query, $parameter, $clob));
1131
    }
1132
 
1133
    // }}}
1134
    // {{{ getBlobValue()
1135
 
1136
    /**
1137
     * Convert a text value into a DBMS specific format that is suitable to
1138
     * compose query statements.
1139
     *
1140
     * @param resource  $prepared_query query handle from prepare()
1141
     * @param           $parameter
1142
     * @param           $blob
1143
     * @return string text string that represents the given argument value in
1144
     *      a DBMS specific format.
1145
     * @access public
1146
     */
1147
    function getBlobValue($prepared_query, $parameter, $blob)
1148
    {
1149
        return($this->_getLobValue($prepared_query, $parameter, $blob));
1150
    }
1151
 
1152
    // }}}
1153
    // {{{ getFloatValue()
1154
 
1155
    /**
1156
     * Convert a text value into a DBMS specific format that is suitable to
1157
     * compose query statements.
1158
     *
1159
     * @param string $value text string value that is intended to be converted.
1160
     * @return string text string that represents the given argument value in
1161
     *      a DBMS specific format.
1162
     * @access public
1163
     */
1164
    function getFloatValue($value)
1165
    {
1166
        return(($value === NULL) ? 'NULL' : $value);
1167
    }
1168
 
1169
    // }}}
1170
    // {{{ getDecimalValue()
1171
 
1172
    /**
1173
     * Convert a text value into a DBMS specific format that is suitable to
1174
     * compose query statements.
1175
     *
1176
     * @param string $value text string value that is intended to be converted.
1177
     * @return string text string that represents the given argument value in
1178
     *      a DBMS specific format.
1179
     * @access public
1180
     */
1181
    function getDecimalValue($value)
1182
    {
1183
        return(($value === NULL) ? 'NULL' : strval(round($value*$this->decimal_factor)));
1184
    }
1185
 
1186
    // }}}
1187
    // {{{ nextId()
1188
 
1189
    /**
1190
     * returns the next free id of a sequence
1191
     *
1192
     * @param string  $seq_name name of the sequence
1193
     * @param boolean $ondemand when TRUE the seqence is
1194
     *                          automatic created, if it
1195
     *                          not exists
1196
     * @return mixed MDB_Error or id
1197
     * @access public
1198
     */
1199
    function nextId($seq_name, $ondemand = TRUE)
1200
    {
1201
        $seqname = $this->getSequenceName($seq_name);
1202
        $repeat = 0;
1203
        do {
1204
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
1205
            $result = $this->query("SELECT NEXTVAL('$seqname')");
1206
            $this->popErrorHandling();
1207
            if ($ondemand && MDB::isError($result) && $result->getCode() == MDB_ERROR_NOSUCHTABLE) {
1208
                $repeat = 1;
1209
                $result = $this->createSequence($seq_name);
1210
                if (MDB::isError($result)) {
1211
                    return($this->raiseError($result));
1212
                }
1213
            } else {
1214
                $repeat = 0;
1215
            }
1216
        } while ($repeat);
1217
        if (MDB::isError($result)) {
1218
            return($this->raiseError($result));
1219
        }
1220
        return($this->fetchOne($result));
1221
    }
1222
 
1223
    // }}}
1224
    // {{{ currId()
1225
 
1226
    /**
1227
     * returns the current id of a sequence
1228
     *
1229
     * @param string  $seq_name name of the sequence
1230
     * @return mixed MDB_Error or id
1231
     * @access public
1232
     */
1233
    function currId($seq_name)
1234
    {
1235
        $seqname = $this->getSequenceName($seq_name);
1236
        if (MDB::isError($result = $this->queryOne("SELECT last_value FROM $seqname"))) {
1237
            return($this->raiseError(MDB_ERROR, NULL, NULL, 'currId: Unable to select from ' . $seqname) );
1238
        }
1239
        if (!is_numeric($result)) {
1240
            return($this->raiseError(MDB_ERROR, NULL, NULL, 'currId: could not find value in sequence table'));
1241
        }
1242
        return($result);
1243
    }
1244
 
1245
    // }}}
1246
    // {{{ fetchInto()
1247
 
1248
    /**
1249
     * Fetch a row and return data in an array.
1250
     *
1251
     * @param resource $result result identifier
1252
     * @param int $fetchmode ignored
1253
     * @param int $rownum the row number to fetch
1254
     * @return mixed data array or NULL on success, a MDB error on failure
1255
     * @access public
1256
     */
1257
    function fetchInto($result, $fetchmode = MDB_FETCHMODE_DEFAULT, $rownum = NULL)
1258
    {
1259
        $result_value = intval($result);
1260
        if ($fetchmode == MDB_FETCHMODE_DEFAULT) {
1261
            $fetchmode = $this->fetchmode;
1262
        }
1263
 
1264
        if (is_null($rownum)) {
1265
            ++$this->highest_fetched_row[$result_value];
1266
        } else {
1267
            $this->highest_fetched_row[$result_value] =
1268
                max($this->highest_fetched_row[$result_value], $rownum);
1269
        }
1270
 
1271
        if ($fetchmode & MDB_FETCHMODE_ASSOC) {
1272
            $row = @pg_fetch_array($result, $rownum, PGSQL_ASSOC);
1273
            if (is_array($row) && $this->options['optimize'] == 'portability') {
1274
                $row = array_change_key_case($row, CASE_LOWER);
1275
            }
1276
        } else {
1277
            $row = @pg_fetch_row($result, $rownum);
1278
        }
1279
 
1280
        if (!$row) {
1281
            if ($this->options['autofree']) {
1282
                $this->freeResult($result);
1283
            }
1284
            return(NULL);
1285
        }
1286
        if (isset($this->result_types[$result_value])) {
1287
            $row = $this->convertResultRow($result, $row);
1288
        }
1289
        return($row);
1290
    }
1291
 
1292
    // }}}
1293
    // {{{ nextResult()
1294
 
1295
    /**
1296
     * Move the internal pgsql result pointer to the next available result
1297
     *
1298
     * @param a valid fbsql result resource
1299
     * @return true if a result is available otherwise return false
1300
     * @access public
1301
     */
1302
    function nextResult($result)
1303
    {
1304
        return(FALSE);
1305
    }
1306
 
1307
    // }}}
1308
    // {{{ tableInfo()
1309
 
1310
    /**
1311
     * returns meta data about the result set
1312
     *
1313
     * @param  mixed $resource PostgreSQL result identifier or table name
1314
     * @param mixed $mode depends on implementation
1315
     * @return array an nested array, or a MDB error
1316
     * @access public
1317
     */
1318
    function tableInfo($result, $mode = NULL)
1319
    {
1320
        $count = 0;
1321
        $id = 0;
1322
        $res = array();
1323
 
1324
        /**
1325
         * depending on $mode, metadata returns the following values:
1326
         *
1327
         * - mode is FALSE (default):
1328
         * $result[]:
1329
         *    [0]['table']  table name
1330
         *    [0]['name']   field name
1331
         *    [0]['type']   field type
1332
         *    [0]['len']    field length
1333
         *    [0]['flags']  field flags
1334
         *
1335
         * - mode is MDB_TABLEINFO_ORDER
1336
         * $result[]:
1337
         *    ['num_fields'] number of metadata records
1338
         *    [0]['table']  table name
1339
         *    [0]['name']   field name
1340
         *    [0]['type']   field type
1341
         *    [0]['len']    field length
1342
         *    [0]['flags']  field flags
1343
         *    ['order'][field name]  index of field named 'field name'
1344
         *    The last one is used, if you have a field name, but no index.
1345
         *    Test:  if (isset($result['meta']['myfield'])) { ...
1346
         *
1347
         * - mode is MDB_TABLEINFO_ORDERTABLE
1348
         *     the same as above. but additionally
1349
         *    ['ordertable'][table name][field name] index of field
1350
         *       named 'field name'
1351
         *
1352
         *       this is, because if you have fields from different
1353
         *       tables with the same field name * they override each
1354
         *       other with MDB_TABLEINFO_ORDER
1355
         *
1356
         *       you can combine MDB_TABLEINFO_ORDER and
1357
         *       MDB_TABLEINFO_ORDERTABLE with MDB_TABLEINFO_ORDER |
1358
         *       MDB_TABLEINFO_ORDERTABLE * or with MDB_TABLEINFO_FULL
1359
         **/
1360
 
1361
        // if $result is a string, then we want information about a
1362
        // table without a resultset
1363
        if (is_string($result)) {
1364
            $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
1365
            if (empty($id)) {
1366
                return($this->pgsqlRaiseError());
1367
            }
1368
        } else { // else we want information about a resultset
1369
            $id = $result;
1370
            if (empty($id)) {
1371
                return($this->pgsqlRaiseError());
1372
            }
1373
        }
1374
 
1375
        $count = @pg_numfields($id);
1376
 
1377
        // made this IF due to performance (one if is faster than $count if's)
1378
        if (empty($mode)) {
1379
            for ($i = 0; $i < $count; $i++) {
1380
                $res[$i]['table'] = (is_string($result)) ? $result : '';
1381
                $res[$i]['name'] = @pg_fieldname ($id, $i);
1382
                $res[$i]['type'] = @pg_fieldtype ($id, $i);
1383
                $res[$i]['len'] = @pg_fieldsize ($id, $i);
1384
                $res[$i]['flags'] = (is_string($result)) ? $this->_pgFieldflags($id, $i, $result) : '';
1385
            }
1386
        } else { // full
1387
            $res['num_fields'] = $count;
1388
 
1389
            for ($i = 0; $i < $count; $i++) {
1390
                $res[$i]['table'] = (is_string($result)) ? $result : '';
1391
                $res[$i]['name'] = @pg_fieldname ($id, $i);
1392
                $res[$i]['type'] = @pg_fieldtype ($id, $i);
1393
                $res[$i]['len'] = @pg_fieldsize ($id, $i);
1394
                $res[$i]['flags'] = (is_string($result)) ? $this->_pgFieldFlags($id, $i, $result) : '';
1395
                if ($mode & MDB_TABLEINFO_ORDER) {
1396
                    $res['order'][$res[$i]['name']] = $i;
1397
                }
1398
                if ($mode & MDB_TABLEINFO_ORDERTABLE) {
1399
                    $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
1400
                }
1401
            }
1402
        }
1403
 
1404
        // free the result only if we were called on a table
1405
        if (is_string($result) && is_resource($id)) {
1406
            @pg_freeresult($id);
1407
        }
1408
        return($res);
1409
    }
1410
 
1411
    // }}}
1412
    // {{{ _pgFieldFlags()
1413
 
1414
    /**
1415
     * Flags of a Field
1416
     *
1417
     * @param int $resource PostgreSQL result identifier
1418
     * @param int $num_field the field number
1419
     * @return string The flags of the field ('not_null', 'default_xx', 'primary_key',
1420
     *                 'unique' and 'multiple_key' are supported)
1421
     * @access private
1422
     **/
1423
    function _pgFieldFlags($resource, $num_field, $table_name)
1424
    {
1425
        $field_name = @pg_fieldname($resource, $num_field);
1426
 
1427
        $result = pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
1428
            FROM pg_attribute f, pg_class tab, pg_type typ
1429
            WHERE tab.relname = typ.typname
1430
            AND typ.typrelid = f.attrelid
1431
            AND f.attname = '$field_name'
1432
            AND tab.relname = '$table_name'");
1433
        if (@pg_numrows($result) > 0) {
1434
            $row = @pg_fetch_row($result, 0);
1435
            $flags = ($row[0] == 't') ? 'not_null ' : '';
1436
 
1437
            if ($row[1] == 't') {
1438
                $result = @pg_exec($this->connection, "SELECT a.adsrc
1439
                    FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a
1440
                    WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
1441
                    AND f.attrelid = a.adrelid AND f.attname = '$field_name'
1442
                    AND tab.relname = '$table_name'");
1443
                $row = @pg_fetch_row($result, 0);
1444
                $num = str_replace('\'', '', $row[0]);
1445
 
1446
                $flags .= "default_$num ";
1447
            }
1448
        }
1449
        $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
1450
            FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i
1451
            WHERE tab.relname = typ.typname
1452
            AND typ.typrelid = f.attrelid
1453
            AND f.attrelid = i.indrelid
1454
            AND f.attname = '$field_name'
1455
            AND tab.relname = '$table_name'");
1456
        $count = @pg_numrows($result);
1457
 
1458
        for ($i = 0; $i < $count ; $i++) {
1459
            $row = @pg_fetch_row($result, $i);
1460
            $keys = explode(' ', $row[2]);
1461
 
1462
            if (in_array($num_field + 1, $keys)) {
1463
                $flags .= ($row[0] == 't') ? 'unique ' : '';
1464
                $flags .= ($row[1] == 't') ? 'primary ' : '';
1465
                if (count($keys) > 1)
1466
                    $flags .= 'multiple_key ';
1467
            }
1468
        }
1469
 
1470
        return trim($flags);
1471
    }
1472
}
1473
 
1474
?>