1 |
<?php |
2 |
// |
3 |
// +----------------------------------------------------------------------+ |
4 |
// | PHP Version 4 | |
5 |
// +----------------------------------------------------------------------+ |
6 |
// | Copyright (c) 1997-2002 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 <rui_hirokawa@ybb.ne.jp> | |
17 |
// | Stig Bakken <ssb@fast.no> | |
18 |
// +----------------------------------------------------------------------+ |
19 |
// |
20 |
// $Id: pgsql.php,v 1.65.2.3 2002/04/10 08:38:42 edink Exp $ |
21 |
// |
22 |
// Database independent query interface definition for PHP's PostgreSQL |
23 |
// extension. |
24 |
// |
25 |
|
26 |
// |
27 |
// XXX legend: |
28 |
// |
29 |
// XXX ERRORMSG: The error message from the pgsql function should |
30 |
// be registered here. |
31 |
// |
32 |
|
33 |
require_once 'DB/common.php'; |
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 |
$this->dsn = $dsninfo; |
87 |
$protocol = (isset($dsninfo['protocol'])) ? $dsninfo['protocol'] : 'tcp'; |
88 |
$connstr = ''; |
89 |
|
90 |
if ($protocol == 'tcp') { |
91 |
if ($dsninfo['hostspec']) { |
92 |
$connstr = 'host=' . $dsninfo['hostspec']; |
93 |
} |
94 |
if ($dsninfo['port']) { |
95 |
$connstr .= ' port=' . $dsninfo['port']; |
96 |
} |
97 |
} |
98 |
|
99 |
if (isset($dsninfo['database'])) { |
100 |
$connstr .= ' dbname=' . $dsninfo['database']; |
101 |
} |
102 |
if (!empty($dsninfo['username'])) { |
103 |
$connstr .= ' user=' . $dsninfo['username']; |
104 |
} |
105 |
if (!empty($dsninfo['password'])) { |
106 |
$connstr .= ' password=' . $dsninfo['password']; |
107 |
} |
108 |
if (!empty($dsninfo['options'])) { |
109 |
$connstr .= ' options=' . $dsninfo['options']; |
110 |
} |
111 |
if (!empty($dsninfo['tty'])) { |
112 |
$connstr .= ' tty=' . $dsninfo['tty']; |
113 |
} |
114 |
|
115 |
$connect_function = $persistent ? 'pg_pconnect' : 'pg_connect'; |
116 |
// catch error |
117 |
ob_start(); |
118 |
$conn = $connect_function($connstr); |
119 |
$error = ob_get_contents(); |
120 |
ob_end_clean(); |
121 |
if ($conn == false) { |
122 |
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, |
123 |
null, null, strip_tags($error)); |
124 |
} |
125 |
$this->connection = $conn; |
126 |
return DB_OK; |
127 |
} |
128 |
|
129 |
// }}} |
130 |
// {{{ disconnect() |
131 |
|
132 |
/** |
133 |
* Log out and disconnect from the database. |
134 |
* |
135 |
* @return bool TRUE on success, FALSE if not connected. |
136 |
*/ |
137 |
function disconnect() |
138 |
{ |
139 |
$ret = @pg_close($this->connection); // XXX ERRORMSG |
140 |
$this->connection = null; |
141 |
return $ret; |
142 |
} |
143 |
|
144 |
// }}} |
145 |
// {{{ simpleQuery() |
146 |
|
147 |
/** |
148 |
* Send a query to PostgreSQL and return the results as a |
149 |
* PostgreSQL resource identifier. |
150 |
* |
151 |
* @param $query the SQL query |
152 |
* |
153 |
* @return int returns a valid PostgreSQL result for successful SELECT |
154 |
* queries, DB_OK for other successful queries. A DB error code |
155 |
* is returned on failure. |
156 |
*/ |
157 |
function simpleQuery($query) |
158 |
{ |
159 |
$ismanip = DB::isManip($query); |
160 |
$this->last_query = $query; |
161 |
$query = $this->modifyQuery($query); |
162 |
if (!$this->autocommit && $ismanip) { |
163 |
if ($this->transaction_opcount == 0) { |
164 |
$result = @pg_exec($this->connection, "begin;"); |
165 |
if (!$result) { |
166 |
return $this->pgsqlRaiseError(); |
167 |
} |
168 |
} |
169 |
$this->transaction_opcount++; |
170 |
} |
171 |
$result = @pg_exec($this->connection, $query); |
172 |
if (!$result) { |
173 |
return $this->pgsqlRaiseError(); |
174 |
} |
175 |
// Determine which queries that should return data, and which |
176 |
// should return an error code only. |
177 |
if ($ismanip) { |
178 |
$this->affected = @pg_cmdtuples($result); |
179 |
return DB_OK; |
180 |
} elseif (preg_match('/^\s*\(?\s*SELECT\s+/si', $query) && |
181 |
!preg_match('/^\s*\(?\s*SELECT\s+INTO\s/si', $query)) { |
182 |
/* PostgreSQL commands: |
183 |
ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY, |
184 |
CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH, |
185 |
GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET, |
186 |
REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW, |
187 |
UNLISTEN, UPDATE, VACUUM |
188 |
*/ |
189 |
$this->row[$result] = 0; // reset the row counter. |
190 |
$numrows = $this->numrows($result); |
191 |
if (is_object($numrows)) { |
192 |
return $numrows; |
193 |
} |
194 |
$this->num_rows[$result] = $numrows; |
195 |
$this->affected = 0; |
196 |
return $result; |
197 |
} else { |
198 |
$this->affected = 0; |
199 |
return DB_OK; |
200 |
} |
201 |
} |
202 |
|
203 |
// }}} |
204 |
// {{{ nextResult() |
205 |
|
206 |
/** |
207 |
* Move the internal pgsql result pointer to the next available result |
208 |
* |
209 |
* @param a valid fbsql result resource |
210 |
* |
211 |
* @access public |
212 |
* |
213 |
* @return true if a result is available otherwise return false |
214 |
*/ |
215 |
function nextResult($result) |
216 |
{ |
217 |
return false; |
218 |
} |
219 |
|
220 |
// }}} |
221 |
// {{{ errorCode() |
222 |
|
223 |
/** |
224 |
* Map native error codes to DB's portable ones. Requires that |
225 |
* the DB implementation's constructor fills in the $errorcode_map |
226 |
* property. |
227 |
* |
228 |
* @param $nativecode the native error code, as returned by the backend |
229 |
* database extension (string or integer) |
230 |
* |
231 |
* @return int a portable DB error code, or FALSE if this DB |
232 |
* implementation has no mapping for the given error code. |
233 |
*/ |
234 |
|
235 |
function errorCode($errormsg) |
236 |
{ |
237 |
static $error_regexps; |
238 |
if (empty($error_regexps)) { |
239 |
$error_regexps = array( |
240 |
'/(Table does not exist\.|Relation [\"\'].*[\"\'] does not exist|sequence does not exist|class ".+" not found)$/' => DB_ERROR_NOSUCHTABLE, |
241 |
'/Relation [\"\'].*[\"\'] already exists|Cannot insert a duplicate key into (a )?unique index.*/' => DB_ERROR_ALREADY_EXISTS, |
242 |
'/divide by zero$/' => DB_ERROR_DIVZERO, |
243 |
'/pg_atoi: error in .*: can\'t parse /' => DB_ERROR_INVALID_NUMBER, |
244 |
'/ttribute [\"\'].*[\"\'] not found$|Relation [\"\'].*[\"\'] does not have attribute [\"\'].*[\"\']/' => DB_ERROR_NOSUCHFIELD, |
245 |
'/parser: parse error at or near \"/' => DB_ERROR_SYNTAX, |
246 |
'/referential integrity violation/' => DB_ERROR_CONSTRAINT |
247 |
); |
248 |
} |
249 |
foreach ($error_regexps as $regexp => $code) { |
250 |
if (preg_match($regexp, $errormsg)) { |
251 |
return $code; |
252 |
} |
253 |
} |
254 |
// Fall back to DB_ERROR if there was no mapping. |
255 |
return DB_ERROR; |
256 |
} |
257 |
|
258 |
// }}} |
259 |
/** |
260 |
* Fetch and return a row of data (it uses fetchInto for that) |
261 |
* @param $result PostgreSQL result identifier |
262 |
* @param $fetchmode format of fetched row array |
263 |
* @param $rownum the absolute row number to fetch |
264 |
* |
265 |
* @return array a row of data, or false on error |
266 |
*/ |
267 |
function fetchRow($result, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null) |
268 |
{ |
269 |
if ($fetchmode == DB_FETCHMODE_DEFAULT) { |
270 |
$fetchmode = $this->fetchmode; |
271 |
} |
272 |
$res = $this->fetchInto ($result, $arr, $fetchmode, $rownum); |
273 |
if ($res !== DB_OK) { |
274 |
return $res; |
275 |
} |
276 |
return $arr; |
277 |
} |
278 |
|
279 |
// {{{ fetchInto() |
280 |
|
281 |
/** |
282 |
* Fetch a row and insert the data into an existing array. |
283 |
* |
284 |
* @param $result PostgreSQL result identifier |
285 |
* @param $row (reference) array where data from the row is stored |
286 |
* @param $fetchmode how the array data should be indexed |
287 |
* @param $rownum the row number to fetch |
288 |
* |
289 |
* @return int DB_OK on success, a DB error code on failure |
290 |
*/ |
291 |
function fetchInto($result, &$row, $fetchmode, $rownum=null) |
292 |
{ |
293 |
$rownum = ($rownum !== null) ? $rownum : $this->row[$result]; |
294 |
if ($rownum >= $this->num_rows[$result]) { |
295 |
return null; |
296 |
} |
297 |
if ($fetchmode & DB_FETCHMODE_ASSOC) { |
298 |
$row = @pg_fetch_array($result, $rownum, PGSQL_ASSOC); |
299 |
} else { |
300 |
$row = @pg_fetch_row($result, $rownum); |
301 |
} |
302 |
if (!$row) { |
303 |
$err = pg_errormessage($this->connection); |
304 |
if (!$err) { |
305 |
return null; |
306 |
} |
307 |
return $this->pgsqlRaiseError(); |
308 |
} |
309 |
$this->row[$result] = ++$rownum; |
310 |
return DB_OK; |
311 |
} |
312 |
|
313 |
// }}} |
314 |
// {{{ freeResult() |
315 |
|
316 |
/** |
317 |
* Free the internal resources associated with $result. |
318 |
* |
319 |
* @param $result int PostgreSQL result identifier or DB statement identifier |
320 |
* |
321 |
* @return bool TRUE on success, FALSE if $result is invalid |
322 |
*/ |
323 |
function freeResult($result) |
324 |
{ |
325 |
if (is_resource($result)) { |
326 |
return @pg_freeresult($result); |
327 |
} |
328 |
if (!isset($this->prepare_tokens[(int)$result])) { |
329 |
return false; |
330 |
} |
331 |
unset($this->prepare_tokens[(int)$result]); |
332 |
unset($this->prepare_types[(int)$result]); |
333 |
unset($this->row[(int)$result]); |
334 |
unset($this->num_rows[(int)$result]); |
335 |
$this->affected = 0; |
336 |
return true; |
337 |
} |
338 |
|
339 |
// }}} |
340 |
// {{{ quote() |
341 |
/** |
342 |
* Quote the given string so it can be safely used within string delimiters |
343 |
* in a query. |
344 |
* @param $string mixed Data to be quoted |
345 |
* @return mixed "NULL" string, quoted string or original data |
346 |
*/ |
347 |
function quote($str = null) |
348 |
{ |
349 |
switch (strtolower(gettype($str))) { |
350 |
case 'null': |
351 |
return 'NULL'; |
352 |
case 'integer': |
353 |
case 'double' : |
354 |
return $str; |
355 |
case 'string': |
356 |
default: |
357 |
$str = str_replace("'", "''", $str); |
358 |
//PostgreSQL treats a backslash as an escape character. |
359 |
$str = str_replace('\\', '\\\\', $str); |
360 |
return "'$str'"; |
361 |
} |
362 |
} |
363 |
// }}} |
364 |
// {{{ numCols() |
365 |
|
366 |
/** |
367 |
* Get the number of columns in a result set. |
368 |
* |
369 |
* @param $result resource PostgreSQL result identifier |
370 |
* |
371 |
* @return int the number of columns per row in $result |
372 |
*/ |
373 |
function numCols($result) |
374 |
{ |
375 |
$cols = @pg_numfields($result); |
376 |
if (!$cols) { |
377 |
return $this->pgsqlRaiseError(); |
378 |
} |
379 |
return $cols; |
380 |
} |
381 |
|
382 |
// }}} |
383 |
// {{{ numRows() |
384 |
|
385 |
/** |
386 |
* Get the number of rows in a result set. |
387 |
* |
388 |
* @param $result resource PostgreSQL result identifier |
389 |
* |
390 |
* @return int the number of rows in $result |
391 |
*/ |
392 |
function numRows($result) |
393 |
{ |
394 |
$rows = @pg_numrows($result); |
395 |
if ($rows === null) { |
396 |
return $this->pgsqlRaiseError(); |
397 |
} |
398 |
return $rows; |
399 |
} |
400 |
|
401 |
// }}} |
402 |
// {{{ errorNative() |
403 |
|
404 |
/** |
405 |
* Get the native error code of the last error (if any) that |
406 |
* occured on the current connection. |
407 |
* |
408 |
* @return int native PostgreSQL error code |
409 |
*/ |
410 |
function errorNative() |
411 |
{ |
412 |
return pg_errormessage($this->connection); |
413 |
} |
414 |
|
415 |
// }}} |
416 |
// {{{ autoCommit() |
417 |
|
418 |
/** |
419 |
* Enable/disable automatic commits |
420 |
*/ |
421 |
function autoCommit($onoff = false) |
422 |
{ |
423 |
// XXX if $this->transaction_opcount > 0, we should probably |
424 |
// issue a warning here. |
425 |
$this->autocommit = $onoff ? true : false; |
426 |
return DB_OK; |
427 |
} |
428 |
|
429 |
// }}} |
430 |
// {{{ commit() |
431 |
|
432 |
/** |
433 |
* Commit the current transaction. |
434 |
*/ |
435 |
function commit() |
436 |
{ |
437 |
if ($this->transaction_opcount > 0) { |
438 |
// (disabled) hack to shut up error messages from libpq.a |
439 |
//@fclose(@fopen("php://stderr", "w")); |
440 |
$result = @pg_exec($this->connection, "end;"); |
441 |
$this->transaction_opcount = 0; |
442 |
if (!$result) { |
443 |
return $this->pgsqlRaiseError(); |
444 |
} |
445 |
} |
446 |
return DB_OK; |
447 |
} |
448 |
|
449 |
// }}} |
450 |
// {{{ rollback() |
451 |
|
452 |
/** |
453 |
* Roll back (undo) the current transaction. |
454 |
*/ |
455 |
function rollback() |
456 |
{ |
457 |
if ($this->transaction_opcount > 0) { |
458 |
$result = @pg_exec($this->connection, "abort;"); |
459 |
$this->transaction_opcount = 0; |
460 |
if (!$result) { |
461 |
return $this->pgsqlRaiseError(); |
462 |
} |
463 |
} |
464 |
return DB_OK; |
465 |
} |
466 |
|
467 |
// }}} |
468 |
// {{{ affectedRows() |
469 |
|
470 |
/** |
471 |
* Gets the number of rows affected by the last query. |
472 |
* if the last query was a select, returns 0. |
473 |
* |
474 |
* @return int number of rows affected by the last query or DB_ERROR |
475 |
*/ |
476 |
function affectedRows() |
477 |
{ |
478 |
return $this->affected; |
479 |
} |
480 |
// }}} |
481 |
// {{{ nextId() |
482 |
|
483 |
/** |
484 |
* Get the next value in a sequence. |
485 |
* |
486 |
* We are using native PostgreSQL sequences. If a sequence does |
487 |
* not exist, it will be created, unless $ondemand is false. |
488 |
* |
489 |
* @access public |
490 |
* @param string $seq_name the name of the sequence |
491 |
* @param bool $ondemand whether to create the sequence on demand |
492 |
* @return a sequence integer, or a DB error |
493 |
*/ |
494 |
function nextId($seq_name, $ondemand = true) |
495 |
{ |
496 |
$seqname = $this->getSequenceName($seq_name); |
497 |
$repeat = 0; |
498 |
do { |
499 |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
500 |
$result = $this->query("SELECT NEXTVAL('${seqname}')"); |
501 |
$this->popErrorHandling(); |
502 |
if ($ondemand && DB::isError($result) && |
503 |
$result->getCode() == DB_ERROR_NOSUCHTABLE) { |
504 |
$repeat = 1; |
505 |
$result = $this->createSequence($seq_name); |
506 |
if (DB::isError($result)) { |
507 |
return $this->raiseError($result); |
508 |
} |
509 |
} else { |
510 |
$repeat = 0; |
511 |
} |
512 |
} while ($repeat); |
513 |
if (DB::isError($result)) { |
514 |
return $this->raiseError($result); |
515 |
} |
516 |
$arr = $result->fetchRow(DB_FETCHMODE_ORDERED); |
517 |
$result->free(); |
518 |
return $arr[0]; |
519 |
} |
520 |
|
521 |
// }}} |
522 |
// {{{ createSequence() |
523 |
|
524 |
/** |
525 |
* Create the sequence |
526 |
* |
527 |
* @param string $seq_name the name of the sequence |
528 |
* @return mixed DB_OK on success or DB error on error |
529 |
* @access public |
530 |
*/ |
531 |
function createSequence($seq_name) |
532 |
{ |
533 |
$seqname = $this->getSequenceName($seq_name); |
534 |
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
535 |
$result = $this->query("CREATE SEQUENCE ${seqname}"); |
536 |
$this->popErrorHandling(); |
537 |
return $result; |
538 |
} |
539 |
|
540 |
// }}} |
541 |
// {{{ dropSequence() |
542 |
|
543 |
/** |
544 |
* Drop a sequence |
545 |
* |
546 |
* @param string $seq_name the name of the sequence |
547 |
* @return mixed DB_OK on success or DB error on error |
548 |
* @access public |
549 |
*/ |
550 |
function dropSequence($seq_name) |
551 |
{ |
552 |
$seqname = $this->getSequenceName($seq_name); |
553 |
return $this->query("DROP SEQUENCE ${seqname}"); |
554 |
} |
555 |
|
556 |
// }}} |
557 |
// {{{ modifyLimitQuery() |
558 |
|
559 |
function modifyLimitQuery($query, $from, $count) |
560 |
{ |
561 |
$query = $query . " LIMIT $count, $from"; |
562 |
return $query; |
563 |
} |
564 |
|
565 |
// }}} |
566 |
// {{{ pgsqlRaiseError() |
567 |
|
568 |
function pgsqlRaiseError($errno = null) |
569 |
{ |
570 |
$native = $this->errorNative(); |
571 |
if ($errno === null) { |
572 |
$err = $this->errorCode($native); |
573 |
} else { |
574 |
$err = $errno; |
575 |
} |
576 |
return $this->raiseError($err, null, null, null, $native); |
577 |
} |
578 |
|
579 |
// }}} |
580 |
// {{{ _pgFieldFlags() |
581 |
|
582 |
/** |
583 |
* Flags of a Field |
584 |
* |
585 |
* @param int $resource PostgreSQL result identifier |
586 |
* @param int $num_field the field number |
587 |
* |
588 |
* @return string The flags of the field ("not_null", "default_xx", "primary_key", |
589 |
* "unique" and "multiple_key" are supported) |
590 |
* @access private |
591 |
*/ |
592 |
function _pgFieldFlags($resource, $num_field, $table_name) |
593 |
{ |
594 |
$field_name = @pg_fieldname($resource, $num_field); |
595 |
|
596 |
$result = pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef |
597 |
FROM pg_attribute f, pg_class tab, pg_type typ |
598 |
WHERE tab.relname = typ.typname |
599 |
AND typ.typrelid = f.attrelid |
600 |
AND f.attname = '$field_name' |
601 |
AND tab.relname = '$table_name'"); |
602 |
if (pg_numrows($result) > 0) { |
603 |
$row = pg_fetch_row($result, 0); |
604 |
$flags = ($row[0] == 't') ? 'not_null ' : ''; |
605 |
|
606 |
if ($row[1] == 't') { |
607 |
$result = pg_exec($this->connection, "SELECT a.adsrc |
608 |
FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a |
609 |
WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid |
610 |
AND f.attrelid = a.adrelid AND f.attname = '$field_name' |
611 |
AND tab.relname = '$table_name'"); |
612 |
$row = pg_fetch_row($result, 0); |
613 |
$num = str_replace('\'', '', $row[0]); |
614 |
|
615 |
$flags .= "default_$num "; |
616 |
} |
617 |
} |
618 |
$result = pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey |
619 |
FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i |
620 |
WHERE tab.relname = typ.typname |
621 |
AND typ.typrelid = f.attrelid |
622 |
AND f.attrelid = i.indrelid |
623 |
AND f.attname = '$field_name' |
624 |
AND tab.relname = '$table_name'"); |
625 |
$count = pg_numrows($result); |
626 |
|
627 |
for ($i = 0; $i < $count ; $i++) { |
628 |
$row = pg_fetch_row($result, $i); |
629 |
$keys = explode(" ", $row[2]); |
630 |
|
631 |
if (in_array($num_field + 1, $keys)) { |
632 |
$flags .= ($row[0] == 't') ? 'unique ' : ''; |
633 |
$flags .= ($row[1] == 't') ? 'primary ' : ''; |
634 |
if (count($keys) > 1) |
635 |
$flags .= 'multiple_key '; |
636 |
} |
637 |
} |
638 |
|
639 |
return trim($flags); |
640 |
} |
641 |
|
642 |
// }}} |
643 |
// {{{ tableInfo() |
644 |
|
645 |
/** |
646 |
* Returns information about a table or a result set |
647 |
* |
648 |
* NOTE: doesn't support table name and flags if called from a db_result |
649 |
* |
650 |
* @param mixed $resource PostgreSQL result identifier or table name |
651 |
* @param int $mode A valid tableInfo mode (DB_TABLEINFO_ORDERTABLE or |
652 |
* DB_TABLEINFO_ORDER) |
653 |
* |
654 |
* @return array An array with all the information |
655 |
*/ |
656 |
function tableInfo($result, $mode = null) |
657 |
{ |
658 |
$count = 0; |
659 |
$id = 0; |
660 |
$res = array(); |
661 |
|
662 |
/* |
663 |
* depending on $mode, metadata returns the following values: |
664 |
* |
665 |
* - mode is false (default): |
666 |
* $result[]: |
667 |
* [0]["table"] table name |
668 |
* [0]["name"] field name |
669 |
* [0]["type"] field type |
670 |
* [0]["len"] field length |
671 |
* [0]["flags"] field flags |
672 |
* |
673 |
* - mode is DB_TABLEINFO_ORDER |
674 |
* $result[]: |
675 |
* ["num_fields"] number of metadata records |
676 |
* [0]["table"] table name |
677 |
* [0]["name"] field name |
678 |
* [0]["type"] field type |
679 |
* [0]["len"] field length |
680 |
* [0]["flags"] field flags |
681 |
* ["order"][field name] index of field named "field name" |
682 |
* The last one is used, if you have a field name, but no index. |
683 |
* Test: if (isset($result['meta']['myfield'])) { ... |
684 |
* |
685 |
* - mode is DB_TABLEINFO_ORDERTABLE |
686 |
* the same as above. but additionally |
687 |
* ["ordertable"][table name][field name] index of field |
688 |
* named "field name" |
689 |
* |
690 |
* this is, because if you have fields from different |
691 |
* tables with the same field name * they override each |
692 |
* other with DB_TABLEINFO_ORDER |
693 |
* |
694 |
* you can combine DB_TABLEINFO_ORDER and |
695 |
* DB_TABLEINFO_ORDERTABLE with DB_TABLEINFO_ORDER | |
696 |
* DB_TABLEINFO_ORDERTABLE * or with DB_TABLEINFO_FULL |
697 |
*/ |
698 |
|
699 |
// if $result is a string, then we want information about a |
700 |
// table without a resultset |
701 |
|
702 |
if (is_string($result)) { |
703 |
$id = pg_exec($this->connection,"SELECT * FROM $result"); |
704 |
if (empty($id)) { |
705 |
return $this->pgsqlRaiseError(); |
706 |
} |
707 |
} else { // else we want information about a resultset |
708 |
$id = $result; |
709 |
if (empty($id)) { |
710 |
return $this->pgsqlRaiseError(); |
711 |
} |
712 |
} |
713 |
|
714 |
$count = @pg_numfields($id); |
715 |
|
716 |
// made this IF due to performance (one if is faster than $count if's) |
717 |
if (empty($mode)) { |
718 |
|
719 |
for ($i=0; $i<$count; $i++) { |
720 |
$res[$i]['table'] = (is_string($result)) ? $result : ''; |
721 |
$res[$i]['name'] = @pg_fieldname ($id, $i); |
722 |
$res[$i]['type'] = @pg_fieldtype ($id, $i); |
723 |
$res[$i]['len'] = @pg_fieldsize ($id, $i); |
724 |
$res[$i]['flags'] = (is_string($result)) ? $this->_pgFieldflags($id, $i, $result) : ''; |
725 |
} |
726 |
|
727 |
} else { // full |
728 |
$res["num_fields"]= $count; |
729 |
|
730 |
for ($i=0; $i<$count; $i++) { |
731 |
$res[$i]['table'] = (is_string($result)) ? $result : ''; |
732 |
$res[$i]['name'] = @pg_fieldname ($id, $i); |
733 |
$res[$i]['type'] = @pg_fieldtype ($id, $i); |
734 |
$res[$i]['len'] = @pg_fieldsize ($id, $i); |
735 |
$res[$i]['flags'] = (is_string($result)) ? $this->_pgFieldFlags($id, $i, $result) : ''; |
736 |
if ($mode & DB_TABLEINFO_ORDER) { |
737 |
$res['order'][$res[$i]['name']] = $i; |
738 |
} |
739 |
if ($mode & DB_TABLEINFO_ORDERTABLE) { |
740 |
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; |
741 |
} |
742 |
} |
743 |
} |
744 |
|
745 |
// free the result only if we were called on a table |
746 |
if (is_resource($id)) { |
747 |
@pg_freeresult($id); |
748 |
} |
749 |
return $res; |
750 |
} |
751 |
|
752 |
// }}} |
753 |
// {{{ getTablesQuery() |
754 |
|
755 |
/** |
756 |
* Returns the query needed to get some backend info |
757 |
* @param string $type What kind of info you want to retrieve |
758 |
* @return string The SQL query string |
759 |
*/ |
760 |
function getSpecialQuery($type) |
761 |
{ |
762 |
switch ($type) { |
763 |
case 'tables': { |
764 |
$sql = "SELECT c.relname as \"Name\" |
765 |
FROM pg_class c, pg_user u |
766 |
WHERE c.relowner = u.usesysid AND c.relkind = 'r' |
767 |
AND not exists (select 1 from pg_views where viewname = c.relname) |
768 |
AND c.relname !~ '^pg_' |
769 |
UNION |
770 |
SELECT c.relname as \"Name\" |
771 |
FROM pg_class c |
772 |
WHERE c.relkind = 'r' |
773 |
AND not exists (select 1 from pg_views where viewname = c.relname) |
774 |
AND not exists (select 1 from pg_user where usesysid = c.relowner) |
775 |
AND c.relname !~ '^pg_'"; |
776 |
break; |
777 |
} |
778 |
case 'views': { |
779 |
// Table cols: viewname | viewowner | definition |
780 |
$sql = "SELECT viewname FROM pg_views"; |
781 |
break; |
782 |
} |
783 |
case 'users': { |
784 |
// cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil |
785 |
$sql = 'SELECT usename FROM pg_user'; |
786 |
break; |
787 |
} |
788 |
case 'databases': { |
789 |
$sql = 'SELECT datname FROM pg_database'; |
790 |
break; |
791 |
} |
792 |
case 'functions': { |
793 |
$sql = 'SELECT proname FROM pg_proc'; |
794 |
break; |
795 |
} |
796 |
default: |
797 |
return null; |
798 |
} |
799 |
return $sql; |
800 |
} |
801 |
|
802 |
// }}} |
803 |
|
804 |
} |
805 |
|
806 |
// Local variables: |
807 |
// tab-width: 4 |
808 |
// c-basic-offset: 4 |
809 |
// End: |
810 |
?> |