~ubuntu-branches/ubuntu/trusty/fusionforge/trusty

« back to all changes in this revision

Viewing changes to plugins/wiki/www/lib/pear/DB/pgsql.php

  • Committer: Bazaar Package Importer
  • Author(s): Roland Mas
  • Date: 2011-04-15 14:55:34 UTC
  • mfrom: (4.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20110415145534-mvn1nochufjmw177
Tags: 5.0.3-1
New upstream bugfix release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
 
3
// +----------------------------------------------------------------------+
 
4
// | PHP Version 4                                                        |
 
5
// +----------------------------------------------------------------------+
 
6
// | Copyright (c) 1997-2004 The PHP Group                                |
 
7
// +----------------------------------------------------------------------+
 
8
// | This source file is subject to version 2.02 of the PHP license,      |
 
9
// | that is bundled with this package in the file LICENSE, and is        |
 
10
// | available at through the world-wide-web at                           |
 
11
// | http://www.php.net/license/2_02.txt.                                 |
 
12
// | If you did not receive a copy of the PHP license and are unable to   |
 
13
// | obtain it through the world-wide-web, please send a note to          |
 
14
// | license@php.net so we can mail you a copy immediately.               |
 
15
// +----------------------------------------------------------------------+
 
16
// | Authors: Rui Hirokawa <hirokawa@php.net>                             |
 
17
// |          Stig Bakken <ssb@php.net>                                   |
 
18
// | Maintainer: Daniel Convissor <danielc@php.net>                       |
 
19
// +----------------------------------------------------------------------+
 
20
//
 
21
// $Id: pgsql.php 6184 2008-08-22 10:33:41Z vargenau $
 
22
 
 
23
require_once 'DB/common.php';
 
24
 
 
25
/**
 
26
 * Database independent query interface definition for PHP's PostgreSQL
 
27
 * extension.
 
28
 *
 
29
 * @package  DB
 
30
 * @version  $Id: pgsql.php 6184 2008-08-22 10:33:41Z vargenau $
 
31
 * @category Database
 
32
 * @author   Rui Hirokawa <hirokawa@php.net>
 
33
 * @author   Stig Bakken <ssb@php.net>
 
34
 */
 
35
class DB_pgsql extends DB_common
 
36
{
 
37
    // {{{ properties
 
38
 
 
39
    var $connection;
 
40
    var $phptype, $dbsyntax;
 
41
    var $prepare_tokens = array();
 
42
    var $prepare_types = array();
 
43
    var $transaction_opcount = 0;
 
44
    var $dsn = array();
 
45
    var $row = array();
 
46
    var $num_rows = array();
 
47
    var $affected = 0;
 
48
    var $autocommit = true;
 
49
    var $fetchmode = DB_FETCHMODE_ORDERED;
 
50
 
 
51
    // }}}
 
52
    // {{{ constructor
 
53
 
 
54
    function DB_pgsql()
 
55
    {
 
56
        $this->DB_common();
 
57
        $this->phptype = 'pgsql';
 
58
        $this->dbsyntax = 'pgsql';
 
59
        $this->features = array(
 
60
            'prepare' => false,
 
61
            'pconnect' => true,
 
62
            'transactions' => true,
 
63
            'limit' => 'alter'
 
64
        );
 
65
        $this->errorcode_map = array(
 
66
        );
 
67
    }
 
68
 
 
69
    // }}}
 
70
    // {{{ connect()
 
71
 
 
72
    /**
 
73
     * Connect to a database and log in as the specified user.
 
74
     *
 
75
     * @param $dsn the data source name (see DB::parseDSN for syntax)
 
76
     * @param $persistent (optional) whether the connection should
 
77
     *        be persistent
 
78
     *
 
79
     * @return int DB_OK on success, a DB error code on failure.
 
80
     */
 
81
    function connect($dsninfo, $persistent = false)
 
82
    {
 
83
        if (!DB::assertExtension('pgsql')) {
 
84
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
 
85
        }
 
86
 
 
87
        $this->dsn = $dsninfo;
 
88
        $protocol = $dsninfo['protocol'] ? $dsninfo['protocol'] : 'tcp';
 
89
        $connstr = '';
 
90
 
 
91
        if ($protocol == 'tcp') {
 
92
            if ($dsninfo['hostspec']) {
 
93
                $connstr .= 'host=' . $dsninfo['hostspec'];
 
94
            }
 
95
            if ($dsninfo['port']) {
 
96
                $connstr .= ' port=' . $dsninfo['port'];
 
97
            }
 
98
        } elseif ($protocol == 'unix') {
 
99
            // Allow for pg socket in non-standard locations.
 
100
            if ($dsninfo['socket']) {
 
101
                $connstr .= 'host=' . $dsninfo['socket'];
 
102
            }
 
103
        }
 
104
 
 
105
        if ($dsninfo['database']) {
 
106
            $connstr .= ' dbname=\'' . addslashes($dsninfo['database']) . '\'';
 
107
        }
 
108
        if ($dsninfo['username']) {
 
109
            $connstr .= ' user=\'' . addslashes($dsninfo['username']) . '\'';
 
110
        }
 
111
        if ($dsninfo['password']) {
 
112
            $connstr .= ' password=\'' . addslashes($dsninfo['password']) . '\'';
 
113
        }
 
114
        if (isset($dsninfo['options'])) {
 
115
            $connstr .= ' options=' . $dsninfo['options'];
 
116
        }
 
117
        if (isset($dsninfo['tty'])) {
 
118
            $connstr .= ' tty=' . $dsninfo['tty'];
 
119
        }
 
120
 
 
121
        $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
 
122
        // catch error
 
123
        ob_start();
 
124
        $conn = $connect_function($connstr);
 
125
        $error = ob_get_contents();
 
126
        ob_end_clean();
 
127
        if ($conn == false) {
 
128
            return $this->raiseError(DB_ERROR_CONNECT_FAILED, null,
 
129
                                     null, null, strip_tags($error));
 
130
        }
 
131
        $this->connection = $conn;
 
132
        return DB_OK;
 
133
    }
 
134
 
 
135
    // }}}
 
136
    // {{{ disconnect()
 
137
 
 
138
    /**
 
139
     * Log out and disconnect from the database.
 
140
     *
 
141
     * @return bool true on success, false if not connected.
 
142
     */
 
143
    function disconnect()
 
144
    {
 
145
        $ret = @pg_close($this->connection);
 
146
        $this->connection = null;
 
147
        return $ret;
 
148
    }
 
149
 
 
150
    // }}}
 
151
    // {{{ simpleQuery()
 
152
 
 
153
    /**
 
154
     * Send a query to PostgreSQL and return the results as a
 
155
     * PostgreSQL resource identifier.
 
156
     *
 
157
     * @param $query the SQL query
 
158
     *
 
159
     * @return int returns a valid PostgreSQL result for successful SELECT
 
160
     * queries, DB_OK for other successful queries.  A DB error code
 
161
     * is returned on failure.
 
162
     */
 
163
    function simpleQuery($query)
 
164
    {
 
165
        $ismanip = DB::isManip($query);
 
166
        $this->last_query = $query;
 
167
        $query = $this->modifyQuery($query);
 
168
        if (!$this->autocommit && $ismanip) {
 
169
            if ($this->transaction_opcount == 0) {
 
170
                $result = @pg_exec($this->connection, 'begin;');
 
171
                if (!$result) {
 
172
                    return $this->pgsqlRaiseError();
 
173
                }
 
174
            }
 
175
            $this->transaction_opcount++;
 
176
        }
 
177
        $result = @pg_exec($this->connection, $query);
 
178
        if (!$result) {
 
179
            return $this->pgsqlRaiseError();
 
180
        }
 
181
        // Determine which queries that should return data, and which
 
182
        // should return an error code only.
 
183
        if ($ismanip) {
 
184
            $this->affected = @pg_cmdtuples($result);
 
185
            return DB_OK;
 
186
        } elseif (preg_match('/^\s*\(?\s*(SELECT(?!\s+INTO)|EXPLAIN|SHOW)\s/si', $query)) {
 
187
            /* PostgreSQL commands:
 
188
               ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
 
189
               CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
 
190
               GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
 
191
               REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
 
192
               UNLISTEN, UPDATE, VACUUM
 
193
            */
 
194
            $this->row[(int)$result] = 0; // reset the row counter.
 
195
            $numrows = $this->numrows($result);
 
196
            if (is_object($numrows)) {
 
197
                return $numrows;
 
198
            }
 
199
            $this->num_rows[(int)$result] = $numrows;
 
200
            $this->affected = 0;
 
201
            return $result;
 
202
        } else {
 
203
            $this->affected = 0;
 
204
            return DB_OK;
 
205
        }
 
206
    }
 
207
 
 
208
    // }}}
 
209
    // {{{ nextResult()
 
210
 
 
211
    /**
 
212
     * Move the internal pgsql result pointer to the next available result
 
213
     *
 
214
     * @param a valid fbsql result resource
 
215
     *
 
216
     * @access public
 
217
     *
 
218
     * @return true if a result is available otherwise return false
 
219
     */
 
220
    function nextResult($result)
 
221
    {
 
222
        return false;
 
223
    }
 
224
 
 
225
    // }}}
 
226
    // {{{ errorCode()
 
227
 
 
228
    /**
 
229
     * Determine PEAR::DB error code from the database's text error message.
 
230
     *
 
231
     * @param  string  $errormsg  error message returned from the database
 
232
     * @return integer  an error number from a DB error constant
 
233
     */
 
234
    function errorCode($errormsg)
 
235
    {
 
236
        static $error_regexps;
 
237
        if (!isset($error_regexps)) {
 
238
            $error_regexps = array(
 
239
                '/(([Rr]elation|[Ss]equence|[Tt]able)( [\"\'].*[\"\'])? does not exist|[Cc]lass ".+" not found)$/' => DB_ERROR_NOSUCHTABLE,
 
240
                '/[Cc]olumn [\"\'].*[\"\'] does not exist/' => DB_ERROR_NOSUCHFIELD,
 
241
                '/[Rr]elation [\"\'].*[\"\'] already exists|[Cc]annot insert a duplicate key into (a )?unique index.*/' => DB_ERROR_ALREADY_EXISTS,
 
242
                '/(divide|division) by zero$/'          => DB_ERROR_DIVZERO,
 
243
                '/pg_atoi: error in .*: can\'t parse /' => DB_ERROR_INVALID_NUMBER,
 
244
                '/invalid input syntax for integer/'    => DB_ERROR_INVALID_NUMBER,
 
245
                '/ttribute [\"\'].*[\"\'] not found$|[Rr]elation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => DB_ERROR_NOSUCHFIELD,
 
246
                '/parser: parse error at or near \"/'   => DB_ERROR_SYNTAX,
 
247
                '/syntax error at/'                     => DB_ERROR_SYNTAX,
 
248
                '/violates not-null constraint/'        => DB_ERROR_CONSTRAINT_NOT_NULL,
 
249
                '/violates [\w ]+ constraint/'          => DB_ERROR_CONSTRAINT,
 
250
                '/referential integrity violation/'     => DB_ERROR_CONSTRAINT
 
251
            );
 
252
        }
 
253
        foreach ($error_regexps as $regexp => $code) {
 
254
            if (preg_match($regexp, $errormsg)) {
 
255
                return $code;
 
256
            }
 
257
        }
 
258
        // Fall back to DB_ERROR if there was no mapping.
 
259
        return DB_ERROR;
 
260
    }
 
261
 
 
262
    // }}}
 
263
    // {{{ fetchInto()
 
264
 
 
265
    /**
 
266
     * Fetch a row and insert the data into an existing array.
 
267
     *
 
268
     * Formating of the array and the data therein are configurable.
 
269
     * See DB_result::fetchInto() for more information.
 
270
     *
 
271
     * @param resource $result    query result identifier
 
272
     * @param array    $arr       (reference) array where data from the row
 
273
     *                            should be placed
 
274
     * @param int      $fetchmode how the resulting array should be indexed
 
275
     * @param int      $rownum    the row number to fetch
 
276
     *
 
277
     * @return mixed DB_OK on success, null when end of result set is
 
278
     *               reached or on failure
 
279
     *
 
280
     * @see DB_result::fetchInto()
 
281
     * @access private
 
282
     */
 
283
    function fetchInto($result, &$arr, $fetchmode, $rownum=null)
 
284
    {
 
285
        $rownum = ($rownum !== null) ? $rownum : $this->row[$result];
 
286
        if ($rownum >= $this->num_rows[$result]) {
 
287
            return null;
 
288
        }
 
289
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
 
290
            $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC);
 
291
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
 
292
                $arr = array_change_key_case($arr, CASE_LOWER);
 
293
            }
 
294
        } else {
 
295
            $arr = @pg_fetch_row($result, $rownum);
 
296
        }
 
297
        if (!$arr) {
 
298
            $err = pg_errormessage($this->connection);
 
299
            if (!$err) {
 
300
                return null;
 
301
            }
 
302
            return $this->pgsqlRaiseError();
 
303
        }
 
304
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
 
305
            $this->_rtrimArrayValues($arr);
 
306
        }
 
307
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
 
308
            $this->_convertNullArrayValuesToEmpty($arr);
 
309
        }
 
310
        $this->row[$result] = ++$rownum;
 
311
        return DB_OK;
 
312
    }
 
313
 
 
314
    // }}}
 
315
    // {{{ freeResult()
 
316
 
 
317
    /**
 
318
     * Free the internal resources associated with $result.
 
319
     *
 
320
     * @param $result int PostgreSQL result identifier
 
321
     *
 
322
     * @return bool true on success, false if $result is invalid
 
323
     */
 
324
    function freeResult($result)
 
325
    {
 
326
        if (is_resource($result)) {
 
327
            unset($this->row[(int)$result]);
 
328
            unset($this->num_rows[(int)$result]);
 
329
            $this->affected = 0;
 
330
            return @pg_freeresult($result);
 
331
        }
 
332
        return false;
 
333
    }
 
334
 
 
335
    // }}}
 
336
    // {{{ quote()
 
337
 
 
338
    /**
 
339
     * @deprecated  Deprecated in release 1.6.0
 
340
     * @internal
 
341
     */
 
342
    function quote($str) {
 
343
        return $this->quoteSmart($str);
 
344
    }
 
345
 
 
346
    // }}}
 
347
    // {{{ quoteSmart()
 
348
 
 
349
    /**
 
350
     * Format input so it can be safely used in a query
 
351
     *
 
352
     * @param mixed $in  data to be quoted
 
353
     *
 
354
     * @return mixed Submitted variable's type = returned value:
 
355
     *               + null = the string <samp>NULL</samp>
 
356
     *               + boolean = string <samp>TRUE</samp> or <samp>FALSE</samp>
 
357
     *               + integer or double = the unquoted number
 
358
     *               + other (including strings and numeric strings) =
 
359
     *                 the data escaped according to MySQL's settings
 
360
     *                 then encapsulated between single quotes
 
361
     *
 
362
     * @internal
 
363
     */
 
364
    function quoteSmart($in)
 
365
    {
 
366
        if (is_int($in) || is_double($in)) {
 
367
            return $in;
 
368
        } elseif (is_bool($in)) {
 
369
            return $in ? 'TRUE' : 'FALSE';
 
370
        } elseif (is_null($in)) {
 
371
            return 'NULL';
 
372
        } else {
 
373
            return "'" . $this->escapeSimple($in) . "'";
 
374
        }
 
375
    }
 
376
 
 
377
    // }}}
 
378
    // {{{ escapeSimple()
 
379
 
 
380
    /**
 
381
     * Escape a string according to the current DBMS's standards
 
382
     *
 
383
     * PostgreSQL treats a backslash as an escape character, so they are
 
384
     * removed.
 
385
     *
 
386
     * Not using pg_escape_string() yet because it requires PostgreSQL
 
387
     * to be at version 7.2 or greater.
 
388
     *
 
389
     * @param string $str  the string to be escaped
 
390
     *
 
391
     * @return string  the escaped string
 
392
     *
 
393
     * @internal
 
394
     */
 
395
    function escapeSimple($str) {
 
396
        return str_replace("'", "''", str_replace('\\', '\\\\', $str));
 
397
    }
 
398
 
 
399
    // }}}
 
400
    // {{{ numCols()
 
401
 
 
402
    /**
 
403
     * Get the number of columns in a result set.
 
404
     *
 
405
     * @param $result resource PostgreSQL result identifier
 
406
     *
 
407
     * @return int the number of columns per row in $result
 
408
     */
 
409
    function numCols($result)
 
410
    {
 
411
        $cols = @pg_numfields($result);
 
412
        if (!$cols) {
 
413
            return $this->pgsqlRaiseError();
 
414
        }
 
415
        return $cols;
 
416
    }
 
417
 
 
418
    // }}}
 
419
    // {{{ numRows()
 
420
 
 
421
    /**
 
422
     * Get the number of rows in a result set.
 
423
     *
 
424
     * @param $result resource PostgreSQL result identifier
 
425
     *
 
426
     * @return int the number of rows in $result
 
427
     */
 
428
    function numRows($result)
 
429
    {
 
430
        $rows = @pg_numrows($result);
 
431
        if ($rows === null) {
 
432
            return $this->pgsqlRaiseError();
 
433
        }
 
434
        return $rows;
 
435
    }
 
436
 
 
437
    // }}}
 
438
    // {{{ errorNative()
 
439
 
 
440
    /**
 
441
     * Get the native error code of the last error (if any) that
 
442
     * occured on the current connection.
 
443
     *
 
444
     * @return int native PostgreSQL error code
 
445
     */
 
446
    function errorNative()
 
447
    {
 
448
        return pg_errormessage($this->connection);
 
449
    }
 
450
 
 
451
    // }}}
 
452
    // {{{ autoCommit()
 
453
 
 
454
    /**
 
455
     * Enable/disable automatic commits
 
456
     */
 
457
    function autoCommit($onoff = false)
 
458
    {
 
459
        // XXX if $this->transaction_opcount > 0, we should probably
 
460
        // issue a warning here.
 
461
        $this->autocommit = $onoff ? true : false;
 
462
        return DB_OK;
 
463
    }
 
464
 
 
465
    // }}}
 
466
    // {{{ commit()
 
467
 
 
468
    /**
 
469
     * Commit the current transaction.
 
470
     */
 
471
    function commit()
 
472
    {
 
473
        if ($this->transaction_opcount > 0) {
 
474
            // (disabled) hack to shut up error messages from libpq.a
 
475
            //@fclose(@fopen("php://stderr", "w"));
 
476
            $result = @pg_exec($this->connection, 'end;');
 
477
            $this->transaction_opcount = 0;
 
478
            if (!$result) {
 
479
                return $this->pgsqlRaiseError();
 
480
            }
 
481
        }
 
482
        return DB_OK;
 
483
    }
 
484
 
 
485
    // }}}
 
486
    // {{{ rollback()
 
487
 
 
488
    /**
 
489
     * Roll back (undo) the current transaction.
 
490
     */
 
491
    function rollback()
 
492
    {
 
493
        if ($this->transaction_opcount > 0) {
 
494
            $result = @pg_exec($this->connection, 'abort;');
 
495
            $this->transaction_opcount = 0;
 
496
            if (!$result) {
 
497
                return $this->pgsqlRaiseError();
 
498
            }
 
499
        }
 
500
        return DB_OK;
 
501
    }
 
502
 
 
503
    // }}}
 
504
    // {{{ affectedRows()
 
505
 
 
506
    /**
 
507
     * Gets the number of rows affected by the last query.
 
508
     * if the last query was a select, returns 0.
 
509
     *
 
510
     * @return int number of rows affected by the last query or DB_ERROR
 
511
     */
 
512
    function affectedRows()
 
513
    {
 
514
        return $this->affected;
 
515
    }
 
516
 
 
517
    // }}}
 
518
    // {{{ nextId()
 
519
 
 
520
    /**
 
521
     * Returns the next free id in a sequence
 
522
     *
 
523
     * @param string  $seq_name  name of the sequence
 
524
     * @param boolean $ondemand  when true, the seqence is automatically
 
525
     *                           created if it does not exist
 
526
     *
 
527
     * @return int  the next id number in the sequence.  DB_Error if problem.
 
528
     *
 
529
     * @internal
 
530
     * @see DB_common::nextID()
 
531
     * @access public
 
532
     */
 
533
    function nextId($seq_name, $ondemand = true)
 
534
    {
 
535
        $seqname = $this->getSequenceName($seq_name);
 
536
        $repeat = false;
 
537
        do {
 
538
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
 
539
            $result =& $this->query("SELECT NEXTVAL('${seqname}')");
 
540
            $this->popErrorHandling();
 
541
            if ($ondemand && DB::isError($result) &&
 
542
                $result->getCode() == DB_ERROR_NOSUCHTABLE) {
 
543
                $repeat = true;
 
544
                $this->pushErrorHandling(PEAR_ERROR_RETURN);
 
545
                $result = $this->createSequence($seq_name);
 
546
                $this->popErrorHandling();
 
547
                if (DB::isError($result)) {
 
548
                    return $this->raiseError($result);
 
549
                }
 
550
            } else {
 
551
                $repeat = false;
 
552
            }
 
553
        } while ($repeat);
 
554
        if (DB::isError($result)) {
 
555
            return $this->raiseError($result);
 
556
        }
 
557
        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
 
558
        $result->free();
 
559
        return $arr[0];
 
560
    }
 
561
 
 
562
    // }}}
 
563
    // {{{ createSequence()
 
564
 
 
565
    /**
 
566
     * Create the sequence
 
567
     *
 
568
     * @param string $seq_name the name of the sequence
 
569
     * @return mixed DB_OK on success or DB error on error
 
570
     * @access public
 
571
     */
 
572
    function createSequence($seq_name)
 
573
    {
 
574
        $seqname = $this->getSequenceName($seq_name);
 
575
        $result = $this->query("CREATE SEQUENCE ${seqname}");
 
576
        return $result;
 
577
    }
 
578
 
 
579
    // }}}
 
580
    // {{{ dropSequence()
 
581
 
 
582
    /**
 
583
     * Drop a sequence
 
584
     *
 
585
     * @param string $seq_name the name of the sequence
 
586
     * @return mixed DB_OK on success or DB error on error
 
587
     * @access public
 
588
     */
 
589
    function dropSequence($seq_name)
 
590
    {
 
591
        $seqname = $this->getSequenceName($seq_name);
 
592
        return $this->query("DROP SEQUENCE ${seqname}");
 
593
    }
 
594
 
 
595
    // }}}
 
596
    // {{{ modifyLimitQuery()
 
597
 
 
598
    function modifyLimitQuery($query, $from, $count)
 
599
    {
 
600
        $query = $query . " LIMIT $count OFFSET $from";
 
601
        return $query;
 
602
    }
 
603
 
 
604
    // }}}
 
605
    // {{{ pgsqlRaiseError()
 
606
 
 
607
    /**
 
608
     * Gather information about an error, then use that info to create a
 
609
     * DB error object and finally return that object.
 
610
     *
 
611
     * @param  integer  $errno  PEAR error number (usually a DB constant) if
 
612
     *                          manually raising an error
 
613
     * @return object  DB error object
 
614
     * @see errorNative()
 
615
     * @see errorCode()
 
616
     * @see DB_common::raiseError()
 
617
     */
 
618
    function pgsqlRaiseError($errno = null)
 
619
    {
 
620
        $native = $this->errorNative();
 
621
        if ($errno === null) {
 
622
            $err = $this->errorCode($native);
 
623
        } else {
 
624
            $err = $errno;
 
625
        }
 
626
        return $this->raiseError($err, null, null, null, $native);
 
627
    }
 
628
 
 
629
    // }}}
 
630
    // {{{ _pgFieldFlags()
 
631
 
 
632
    /**
 
633
     * Flags of a Field
 
634
     *
 
635
     * @param int $resource PostgreSQL result identifier
 
636
     * @param int $num_field the field number
 
637
     *
 
638
     * @return string The flags of the field ("not_null", "default_value",
 
639
     *                "primary_key", "unique_key" and "multiple_key"
 
640
     *                are supported).  The default value is passed
 
641
     *                through rawurlencode() in case there are spaces in it.
 
642
     * @access private
 
643
     */
 
644
    function _pgFieldFlags($resource, $num_field, $table_name)
 
645
    {
 
646
        $field_name = @pg_fieldname($resource, $num_field);
 
647
 
 
648
        $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
 
649
                                FROM pg_attribute f, pg_class tab, pg_type typ
 
650
                                WHERE tab.relname = typ.typname
 
651
                                AND typ.typrelid = f.attrelid
 
652
                                AND f.attname = '$field_name'
 
653
                                AND tab.relname = '$table_name'");
 
654
        if (@pg_numrows($result) > 0) {
 
655
            $row = @pg_fetch_row($result, 0);
 
656
            $flags  = ($row[0] == 't') ? 'not_null ' : '';
 
657
 
 
658
            if ($row[1] == 't') {
 
659
                $result = @pg_exec($this->connection, "SELECT a.adsrc
 
660
                                    FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a
 
661
                                    WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
 
662
                                    AND f.attrelid = a.adrelid AND f.attname = '$field_name'
 
663
                                    AND tab.relname = '$table_name' AND f.attnum = a.adnum");
 
664
                $row = @pg_fetch_row($result, 0);
 
665
                $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]);
 
666
                $flags .= 'default_' . rawurlencode($num) . ' ';
 
667
            }
 
668
        } else {
 
669
            $flags = '';
 
670
        }
 
671
        $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
 
672
                                FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i
 
673
                                WHERE tab.relname = typ.typname
 
674
                                AND typ.typrelid = f.attrelid
 
675
                                AND f.attrelid = i.indrelid
 
676
                                AND f.attname = '$field_name'
 
677
                                AND tab.relname = '$table_name'");
 
678
        $count = @pg_numrows($result);
 
679
 
 
680
        for ($i = 0; $i < $count ; $i++) {
 
681
            $row = @pg_fetch_row($result, $i);
 
682
            $keys = explode(' ', $row[2]);
 
683
 
 
684
            if (in_array($num_field + 1, $keys)) {
 
685
                $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : '';
 
686
                $flags .= ($row[1] == 't') ? 'primary_key ' : '';
 
687
                if (count($keys) > 1)
 
688
                    $flags .= 'multiple_key ';
 
689
            }
 
690
        }
 
691
 
 
692
        return trim($flags);
 
693
    }
 
694
 
 
695
    // }}}
 
696
    // {{{ tableInfo()
 
697
 
 
698
    /**
 
699
     * Returns information about a table or a result set.
 
700
     *
 
701
     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
 
702
     * is a table name.
 
703
     *
 
704
     * @param object|string  $result  DB_result object from a query or a
 
705
     *                                string containing the name of a table
 
706
     * @param int            $mode    a valid tableInfo mode
 
707
     * @return array  an associative array with the information requested
 
708
     *                or an error object if something is wrong
 
709
     * @access public
 
710
     * @internal
 
711
     * @see DB_common::tableInfo()
 
712
     */
 
713
    function tableInfo($result, $mode = null)
 
714
    {
 
715
        if (isset($result->result)) {
 
716
            /*
 
717
             * Probably received a result object.
 
718
             * Extract the result resource identifier.
 
719
             */
 
720
            $id = $result->result;
 
721
            $got_string = false;
 
722
        } elseif (is_string($result)) {
 
723
            /*
 
724
             * Probably received a table name.
 
725
             * Create a result resource identifier.
 
726
             */
 
727
            $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
 
728
            $got_string = true;
 
729
        } else {
 
730
            /*
 
731
             * Probably received a result resource identifier.
 
732
             * Copy it.
 
733
             * Deprecated.  Here for compatibility only.
 
734
             */
 
735
            $id = $result;
 
736
            $got_string = false;
 
737
        }
 
738
 
 
739
        if (!is_resource($id)) {
 
740
            return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
 
741
        }
 
742
 
 
743
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
 
744
            $case_func = 'strtolower';
 
745
        } else {
 
746
            $case_func = 'strval';
 
747
        }
 
748
 
 
749
        $count = @pg_numfields($id);
 
750
 
 
751
        // made this IF due to performance (one if is faster than $count if's)
 
752
        if (!$mode) {
 
753
 
 
754
            for ($i=0; $i<$count; $i++) {
 
755
                $res[$i]['table'] = $got_string ? $case_func($result) : '';
 
756
                $res[$i]['name']  = $case_func(@pg_fieldname($id, $i));
 
757
                $res[$i]['type']  = @pg_fieldtype($id, $i);
 
758
                $res[$i]['len']   = @pg_fieldsize($id, $i);
 
759
                $res[$i]['flags'] = $got_string ? $this->_pgFieldflags($id, $i, $result) : '';
 
760
            }
 
761
 
 
762
        } else { // full
 
763
            $res['num_fields']= $count;
 
764
 
 
765
            for ($i=0; $i<$count; $i++) {
 
766
                $res[$i]['table'] = $got_string ? $case_func($result) : '';
 
767
                $res[$i]['name']  = $case_func(@pg_fieldname($id, $i));
 
768
                $res[$i]['type']  = @pg_fieldtype($id, $i);
 
769
                $res[$i]['len']   = @pg_fieldsize($id, $i);
 
770
                $res[$i]['flags'] = $got_string ? $this->_pgFieldFlags($id, $i, $result) : '';
 
771
 
 
772
                if ($mode & DB_TABLEINFO_ORDER) {
 
773
                    $res['order'][$res[$i]['name']] = $i;
 
774
                }
 
775
                if ($mode & DB_TABLEINFO_ORDERTABLE) {
 
776
                    $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
 
777
                }
 
778
            }
 
779
        }
 
780
 
 
781
        // free the result only if we were called on a table
 
782
        if ($got_string) {
 
783
            @pg_freeresult($id);
 
784
        }
 
785
        return $res;
 
786
    }
 
787
 
 
788
    // }}}
 
789
    // {{{ getTablesQuery()
 
790
 
 
791
    /**
 
792
     * Returns the query needed to get some backend info
 
793
     * @param string $type What kind of info you want to retrieve
 
794
     * @return string The SQL query string
 
795
     */
 
796
    function getSpecialQuery($type)
 
797
    {
 
798
        switch ($type) {
 
799
            case 'tables':
 
800
                return "SELECT c.relname as \"Name\"
 
801
                        FROM pg_class c, pg_user u
 
802
                        WHERE c.relowner = u.usesysid AND c.relkind = 'r'
 
803
                        AND not exists (select 1 from pg_views where viewname = c.relname)
 
804
                        AND c.relname !~ '^pg_'
 
805
                        UNION
 
806
                        SELECT c.relname as \"Name\"
 
807
                        FROM pg_class c
 
808
                        WHERE c.relkind = 'r'
 
809
                        AND not exists (select 1 from pg_views where viewname = c.relname)
 
810
                        AND not exists (select 1 from pg_user where usesysid = c.relowner)
 
811
                        AND c.relname !~ '^pg_'";
 
812
            case 'views':
 
813
                // Table cols: viewname | viewowner | definition
 
814
                return 'SELECT viewname FROM pg_views';
 
815
            case 'users':
 
816
                // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd  |valuntil
 
817
                return 'SELECT usename FROM pg_user';
 
818
            case 'databases':
 
819
                return 'SELECT datname FROM pg_database';
 
820
            case 'functions':
 
821
                return 'SELECT proname FROM pg_proc';
 
822
            default:
 
823
                return null;
 
824
        }
 
825
    }
 
826
 
 
827
    // }}}
 
828
 
 
829
}
 
830
 
 
831
/*
 
832
 * Local variables:
 
833
 * tab-width: 4
 
834
 * c-basic-offset: 4
 
835
 * End:
 
836
 */
 
837
 
 
838
?>