pgsql.php 27 KB

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