pgsql.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * The PEAR DB driver for PHP's pgsql extension
  5. * for interacting with PostgreSQL databases
  6. *
  7. * PHP version 5
  8. *
  9. * LICENSE: This source file is subject to version 3.0 of the PHP license
  10. * that is available through the world-wide-web at the following URI:
  11. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  12. * the PHP License and are unable to obtain it through the web, please
  13. * send a note to license@php.net so we can mail you a copy immediately.
  14. *
  15. * @category Database
  16. * @package DB
  17. * @author Rui Hirokawa <hirokawa@php.net>
  18. * @author Stig Bakken <ssb@php.net>
  19. * @author Daniel Convissor <danielc@php.net>
  20. * @copyright 1997-2007 The PHP Group
  21. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  22. * @version CVS: $Id$
  23. * @link http://pear.php.net/package/DB
  24. */
  25. /**
  26. * Obtain the DB_common class so it can be extended from
  27. */
  28. require_once 'DB/common.php';
  29. /**
  30. * The methods PEAR DB uses to interact with PHP's pgsql extension
  31. * for interacting with PostgreSQL databases
  32. *
  33. * These methods overload the ones declared in DB_common.
  34. *
  35. * @category Database
  36. * @package DB
  37. * @author Rui Hirokawa <hirokawa@php.net>
  38. * @author Stig Bakken <ssb@php.net>
  39. * @author Daniel Convissor <danielc@php.net>
  40. * @copyright 1997-2007 The PHP Group
  41. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  42. * @version Release: 1.9.2
  43. * @link http://pear.php.net/package/DB
  44. */
  45. class DB_pgsql extends DB_common
  46. {
  47. // {{{ properties
  48. /**
  49. * The DB driver type (mysql, oci8, odbc, etc.)
  50. * @var string
  51. */
  52. public $phptype = 'pgsql';
  53. /**
  54. * The database syntax variant to be used (db2, access, etc.), if any
  55. * @var string
  56. */
  57. public $dbsyntax = 'pgsql';
  58. /**
  59. * The capabilities of this DB implementation
  60. *
  61. * The 'new_link' element contains the PHP version that first provided
  62. * new_link support for this DBMS. Contains false if it's unsupported.
  63. *
  64. * Meaning of the 'limit' element:
  65. * + 'emulate' = emulate with fetch row by number
  66. * + 'alter' = alter the query
  67. * + false = skip rows
  68. *
  69. * @var array
  70. */
  71. public $features = array(
  72. 'limit' => 'alter',
  73. 'new_link' => '4.3.0',
  74. 'numrows' => true,
  75. 'pconnect' => true,
  76. 'prepare' => false,
  77. 'ssl' => true,
  78. 'transactions' => true,
  79. );
  80. /**
  81. * A mapping of native error codes to DB error codes
  82. * @var array
  83. */
  84. public $errorcode_map = array(
  85. );
  86. /**
  87. * The raw database connection created by PHP
  88. * @var resource
  89. */
  90. public $connection;
  91. /**
  92. * The DSN information for connecting to a database
  93. * @var array
  94. */
  95. public $dsn = array();
  96. /**
  97. * Should data manipulation queries be committed automatically?
  98. * @var bool
  99. * @access private
  100. */
  101. public $autocommit = true;
  102. /**
  103. * The quantity of transactions begun
  104. *
  105. * {@internal While this is private, it can't actually be designated
  106. * private in PHP 5 because it is directly accessed in the test suite.}}
  107. *
  108. * @var integer
  109. * @access private
  110. */
  111. public $transaction_opcount = 0;
  112. /**
  113. * The number of rows affected by a data manipulation query
  114. * @var integer
  115. */
  116. public $affected = 0;
  117. /**
  118. * The current row being looked at in fetchInto()
  119. * @var array
  120. * @access private
  121. */
  122. public $row = array();
  123. /**
  124. * The number of rows in a given result set
  125. * @var array
  126. * @access private
  127. */
  128. public $_num_rows = array();
  129. // }}}
  130. // {{{ constructor
  131. /**
  132. * This constructor calls <kbd>parent::__construct()</kbd>
  133. *
  134. * @return void
  135. */
  136. public function __construct()
  137. {
  138. parent::__construct();
  139. }
  140. // }}}
  141. // {{{ connect()
  142. /**
  143. * Connect to the database server, log in and open the database
  144. *
  145. * Don't call this method directly. Use DB::connect() instead.
  146. *
  147. * PEAR DB's pgsql driver supports the following extra DSN options:
  148. * + connect_timeout How many seconds to wait for a connection to
  149. * be established. Available since PEAR DB 1.7.0.
  150. * + new_link If set to true, causes subsequent calls to
  151. * connect() to return a new connection link
  152. * instead of the existing one. WARNING: this is
  153. * not portable to other DBMS's. Available only
  154. * if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0.
  155. * + options Command line options to be sent to the server.
  156. * Available since PEAR DB 1.6.4.
  157. * + service Specifies a service name in pg_service.conf that
  158. * holds additional connection parameters.
  159. * Available since PEAR DB 1.7.0.
  160. * + sslmode How should SSL be used when connecting? Values:
  161. * disable, allow, prefer or require.
  162. * Available since PEAR DB 1.7.0.
  163. * + tty This was used to specify where to send server
  164. * debug output. Available since PEAR DB 1.6.4.
  165. *
  166. * Example of connecting to a new link via a socket:
  167. * <code>
  168. * require_once 'DB.php';
  169. *
  170. * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true';
  171. * $options = array(
  172. * 'portability' => DB_PORTABILITY_ALL,
  173. * );
  174. *
  175. * $db = DB::connect($dsn, $options);
  176. * if (PEAR::isError($db)) {
  177. * die($db->getMessage());
  178. * }
  179. * </code>
  180. *
  181. * @param array $dsn the data source name
  182. * @param bool $persistent should the connection be persistent?
  183. *
  184. * @return int DB_OK on success. A DB_Error object on failure.
  185. *
  186. * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT
  187. */
  188. public function connect($dsn, $persistent = false)
  189. {
  190. if (!PEAR::loadExtension('pgsql')) {
  191. return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  192. }
  193. $this->dsn = $dsn;
  194. if ($dsn['dbsyntax']) {
  195. $this->dbsyntax = $dsn['dbsyntax'];
  196. }
  197. $protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp';
  198. $params = array('');
  199. if ($protocol == 'tcp') {
  200. if ($dsn['hostspec']) {
  201. $params[0] .= 'host=' . $dsn['hostspec'];
  202. }
  203. if ($dsn['port']) {
  204. $params[0] .= ' port=' . $dsn['port'];
  205. }
  206. } elseif ($protocol == 'unix') {
  207. // Allow for pg socket in non-standard locations.
  208. if ($dsn['socket']) {
  209. $params[0] .= 'host=' . $dsn['socket'];
  210. }
  211. if ($dsn['port']) {
  212. $params[0] .= ' port=' . $dsn['port'];
  213. }
  214. }
  215. if ($dsn['database']) {
  216. $params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\'';
  217. }
  218. if ($dsn['username']) {
  219. $params[0] .= ' user=\'' . addslashes($dsn['username']) . '\'';
  220. }
  221. if ($dsn['password']) {
  222. $params[0] .= ' password=\'' . addslashes($dsn['password']) . '\'';
  223. }
  224. if (!empty($dsn['options'])) {
  225. $params[0] .= ' options=' . $dsn['options'];
  226. }
  227. if (!empty($dsn['tty'])) {
  228. $params[0] .= ' tty=' . $dsn['tty'];
  229. }
  230. if (!empty($dsn['connect_timeout'])) {
  231. $params[0] .= ' connect_timeout=' . $dsn['connect_timeout'];
  232. }
  233. if (!empty($dsn['sslmode'])) {
  234. $params[0] .= ' sslmode=' . $dsn['sslmode'];
  235. }
  236. if (!empty($dsn['service'])) {
  237. $params[0] .= ' service=' . $dsn['service'];
  238. }
  239. if (isset($dsn['new_link'])
  240. && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) {
  241. if (version_compare(phpversion(), '4.3.0', '>=')) {
  242. $params[] = PGSQL_CONNECT_FORCE_NEW;
  243. }
  244. }
  245. $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
  246. $ini = ini_get('track_errors');
  247. $php_errormsg = '';
  248. if ($ini) {
  249. $this->connection = @call_user_func_array(
  250. $connect_function,
  251. $params
  252. );
  253. } else {
  254. @ini_set('track_errors', 1);
  255. $this->connection = @call_user_func_array(
  256. $connect_function,
  257. $params
  258. );
  259. @ini_set('track_errors', $ini);
  260. }
  261. if (!$this->connection) {
  262. return $this->raiseError(
  263. DB_ERROR_CONNECT_FAILED,
  264. null,
  265. null,
  266. null,
  267. $php_errormsg
  268. );
  269. }
  270. return DB_OK;
  271. }
  272. // }}}
  273. // {{{ disconnect()
  274. /**
  275. * Disconnects from the database server
  276. *
  277. * @return bool TRUE on success, FALSE on failure
  278. */
  279. public function disconnect()
  280. {
  281. $ret = @pg_close($this->connection);
  282. $this->connection = null;
  283. return $ret;
  284. }
  285. // }}}
  286. // {{{ simpleQuery()
  287. /**
  288. * Sends a query to the database server
  289. *
  290. * @param string the SQL query string
  291. *
  292. * @return mixed + a PHP result resrouce for successful SELECT queries
  293. * + the DB_OK constant for other successful queries
  294. * + a DB_Error object on failure
  295. */
  296. public function simpleQuery($query)
  297. {
  298. $ismanip = $this->_checkManip($query);
  299. $this->last_query = $query;
  300. $query = $this->modifyQuery($query);
  301. if (!$this->autocommit && $ismanip) {
  302. if ($this->transaction_opcount == 0) {
  303. $result = @pg_exec($this->connection, 'begin;');
  304. if (!$result) {
  305. return $this->pgsqlRaiseError();
  306. }
  307. }
  308. $this->transaction_opcount++;
  309. }
  310. $result = @pg_exec($this->connection, $query);
  311. if (!$result) {
  312. return $this->pgsqlRaiseError();
  313. }
  314. /*
  315. * Determine whether queries produce affected rows, result or nothing.
  316. *
  317. * This logic was introduced in version 1.1 of the file by ssb,
  318. * though the regex has been modified slightly since then.
  319. *
  320. * PostgreSQL commands:
  321. * ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY,
  322. * CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH,
  323. * GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET,
  324. * REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW,
  325. * UNLISTEN, UPDATE, VACUUM, WITH
  326. */
  327. if ($ismanip) {
  328. $this->affected = @pg_affected_rows($result);
  329. return DB_OK;
  330. } elseif (preg_match(
  331. '/^\s*\(*\s*(SELECT|EXPLAIN|FETCH|SHOW|WITH)\s/si',
  332. $query
  333. )) {
  334. $this->row[(int)$result] = 0; // reset the row counter.
  335. $numrows = $this->numRows($result);
  336. if (is_object($numrows)) {
  337. return $numrows;
  338. }
  339. $this->_num_rows[(int)$result] = $numrows;
  340. $this->affected = 0;
  341. return $result;
  342. } else {
  343. $this->affected = 0;
  344. return DB_OK;
  345. }
  346. }
  347. // }}}
  348. // {{{ nextResult()
  349. /**
  350. * Move the internal pgsql result pointer to the next available result
  351. *
  352. * @param a valid fbsql result resource
  353. *
  354. * @access public
  355. *
  356. * @return true if a result is available otherwise return false
  357. */
  358. public function nextResult($result)
  359. {
  360. return false;
  361. }
  362. // }}}
  363. // {{{ fetchInto()
  364. /**
  365. * Places a row from the result set into the given array
  366. *
  367. * Formating of the array and the data therein are configurable.
  368. * See DB_result::fetchInto() for more information.
  369. *
  370. * This method is not meant to be called directly. Use
  371. * DB_result::fetchInto() instead. It can't be declared "protected"
  372. * because DB_result is a separate object.
  373. *
  374. * @param resource $result the query result resource
  375. * @param array $arr the referenced array to put the data in
  376. * @param int $fetchmode how the resulting array should be indexed
  377. * @param int $rownum the row number to fetch (0 = first row)
  378. *
  379. * @return mixed DB_OK on success, NULL when the end of a result set is
  380. * reached or on failure
  381. *
  382. * @see DB_result::fetchInto()
  383. */
  384. public function fetchInto($result, &$arr, $fetchmode, $rownum = null)
  385. {
  386. $result_int = (int)$result;
  387. $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int];
  388. if ($rownum >= $this->_num_rows[$result_int]) {
  389. return null;
  390. }
  391. if ($fetchmode & DB_FETCHMODE_ASSOC) {
  392. $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC);
  393. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
  394. $arr = array_change_key_case($arr, CASE_LOWER);
  395. }
  396. } else {
  397. $arr = @pg_fetch_row($result, $rownum);
  398. }
  399. if (!$arr) {
  400. return null;
  401. }
  402. if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
  403. $this->_rtrimArrayValues($arr);
  404. }
  405. if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
  406. $this->_convertNullArrayValuesToEmpty($arr);
  407. }
  408. $this->row[$result_int] = ++$rownum;
  409. return DB_OK;
  410. }
  411. // }}}
  412. // {{{ freeResult()
  413. /**
  414. * Deletes the result set and frees the memory occupied by the result set
  415. *
  416. * This method is not meant to be called directly. Use
  417. * DB_result::free() instead. It can't be declared "protected"
  418. * because DB_result is a separate object.
  419. *
  420. * @param resource $result PHP's query result resource
  421. *
  422. * @return bool TRUE on success, FALSE if $result is invalid
  423. *
  424. * @see DB_result::free()
  425. */
  426. public function freeResult($result)
  427. {
  428. if (is_resource($result)) {
  429. unset($this->row[(int)$result]);
  430. unset($this->_num_rows[(int)$result]);
  431. $this->affected = 0;
  432. return @pg_freeresult($result);
  433. }
  434. return false;
  435. }
  436. // }}}
  437. // {{{ quoteBoolean()
  438. /**
  439. * Formats a boolean value for use within a query in a locale-independent
  440. * manner.
  441. *
  442. * @param boolean the boolean value to be quoted.
  443. * @return string the quoted string.
  444. * @see DB_common::quoteSmart()
  445. * @since Method available since release 1.7.8.
  446. */
  447. public function quoteBoolean($boolean)
  448. {
  449. return $boolean ? 'TRUE' : 'FALSE';
  450. }
  451. // }}}
  452. // {{{ escapeSimple()
  453. /**
  454. * Escapes a string according to the current DBMS's standards
  455. *
  456. * {@internal PostgreSQL treats a backslash as an escape character,
  457. * so they are escaped as well.
  458. *
  459. * @param string $str the string to be escaped
  460. *
  461. * @return string the escaped string
  462. *
  463. * @see DB_common::quoteSmart()
  464. * @since Method available since Release 1.6.0
  465. */
  466. public function escapeSimple($str)
  467. {
  468. if (function_exists('pg_escape_string')) {
  469. /* This fixes an undocumented BC break in PHP 5.2.0 which changed
  470. * the prototype of pg_escape_string. I'm not thrilled about having
  471. * to sniff the PHP version, quite frankly, but it's the only way
  472. * to deal with the problem. Revision 1.331.2.13.2.10 on
  473. * php-src/ext/pgsql/pgsql.c (PHP_5_2 branch) is to blame, for the
  474. * record. */
  475. if (version_compare(PHP_VERSION, '5.2.0', '>=')) {
  476. return pg_escape_string($this->connection, $str);
  477. } else {
  478. return pg_escape_string($str);
  479. }
  480. } else {
  481. return str_replace("'", "''", str_replace('\\', '\\\\', $str));
  482. }
  483. }
  484. // }}}
  485. // {{{ numCols()
  486. /**
  487. * Gets the number of columns in a result set
  488. *
  489. * This method is not meant to be called directly. Use
  490. * DB_result::numCols() instead. It can't be declared "protected"
  491. * because DB_result is a separate object.
  492. *
  493. * @param resource $result PHP's query result resource
  494. *
  495. * @return int the number of columns. A DB_Error object on failure.
  496. *
  497. * @see DB_result::numCols()
  498. */
  499. public function numCols($result)
  500. {
  501. $cols = @pg_numfields($result);
  502. if (!$cols) {
  503. return $this->pgsqlRaiseError();
  504. }
  505. return $cols;
  506. }
  507. // }}}
  508. // {{{ numRows()
  509. /**
  510. * Gets the number of rows in a result set
  511. *
  512. * This method is not meant to be called directly. Use
  513. * DB_result::numRows() instead. It can't be declared "protected"
  514. * because DB_result is a separate object.
  515. *
  516. * @param resource $result PHP's query result resource
  517. *
  518. * @return int the number of rows. A DB_Error object on failure.
  519. *
  520. * @see DB_result::numRows()
  521. */
  522. public function numRows($result)
  523. {
  524. $rows = @pg_numrows($result);
  525. if ($rows === null) {
  526. return $this->pgsqlRaiseError();
  527. }
  528. return $rows;
  529. }
  530. // }}}
  531. // {{{ autoCommit()
  532. /**
  533. * Enables or disables automatic commits
  534. *
  535. * @param bool $onoff true turns it on, false turns it off
  536. *
  537. * @return int DB_OK on success. A DB_Error object if the driver
  538. * doesn't support auto-committing transactions.
  539. */
  540. public function autoCommit($onoff = false)
  541. {
  542. // XXX if $this->transaction_opcount > 0, we should probably
  543. // issue a warning here.
  544. $this->autocommit = $onoff ? true : false;
  545. return DB_OK;
  546. }
  547. // }}}
  548. // {{{ commit()
  549. /**
  550. * Commits the current transaction
  551. *
  552. * @return int DB_OK on success. A DB_Error object on failure.
  553. */
  554. public function commit()
  555. {
  556. if ($this->transaction_opcount > 0) {
  557. // (disabled) hack to shut up error messages from libpq.a
  558. //@fclose(@fopen("php://stderr", "w"));
  559. $result = @pg_exec($this->connection, 'end;');
  560. $this->transaction_opcount = 0;
  561. if (!$result) {
  562. return $this->pgsqlRaiseError();
  563. }
  564. }
  565. return DB_OK;
  566. }
  567. // }}}
  568. // {{{ rollback()
  569. /**
  570. * Reverts the current transaction
  571. *
  572. * @return int DB_OK on success. A DB_Error object on failure.
  573. */
  574. public function rollback()
  575. {
  576. if ($this->transaction_opcount > 0) {
  577. $result = @pg_exec($this->connection, 'abort;');
  578. $this->transaction_opcount = 0;
  579. if (!$result) {
  580. return $this->pgsqlRaiseError();
  581. }
  582. }
  583. return DB_OK;
  584. }
  585. // }}}
  586. // {{{ affectedRows()
  587. /**
  588. * Determines the number of rows affected by a data maniuplation query
  589. *
  590. * 0 is returned for queries that don't manipulate data.
  591. *
  592. * @return int the number of rows. A DB_Error object on failure.
  593. */
  594. public function affectedRows()
  595. {
  596. return $this->affected;
  597. }
  598. // }}}
  599. // {{{ nextId()
  600. /**
  601. * Returns the next free id in a sequence
  602. *
  603. * @param string $seq_name name of the sequence
  604. * @param boolean $ondemand when true, the seqence is automatically
  605. * created if it does not exist
  606. *
  607. * @return int the next id number in the sequence.
  608. * A DB_Error object on failure.
  609. *
  610. * @see DB_common::nextID(), DB_common::getSequenceName(),
  611. * DB_pgsql::createSequence(), DB_pgsql::dropSequence()
  612. */
  613. public function nextId($seq_name, $ondemand = true)
  614. {
  615. $seqname = $this->getSequenceName($seq_name);
  616. $repeat = false;
  617. do {
  618. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  619. $result = $this->query("SELECT NEXTVAL('${seqname}')");
  620. $this->popErrorHandling();
  621. if ($ondemand && DB::isError($result) &&
  622. $result->getCode() == DB_ERROR_NOSUCHTABLE) {
  623. $repeat = true;
  624. $this->pushErrorHandling(PEAR_ERROR_RETURN);
  625. $result = $this->createSequence($seq_name);
  626. $this->popErrorHandling();
  627. if (DB::isError($result)) {
  628. return $this->raiseError($result);
  629. }
  630. } else {
  631. $repeat = false;
  632. }
  633. } while ($repeat);
  634. if (DB::isError($result)) {
  635. return $this->raiseError($result);
  636. }
  637. $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
  638. $result->free();
  639. return $arr[0];
  640. }
  641. // }}}
  642. // {{{ createSequence()
  643. /**
  644. * Creates a new sequence
  645. *
  646. * @param string $seq_name name of the new sequence
  647. *
  648. * @return int DB_OK on success. A DB_Error object on failure.
  649. *
  650. * @see DB_common::createSequence(), DB_common::getSequenceName(),
  651. * DB_pgsql::nextID(), DB_pgsql::dropSequence()
  652. */
  653. public function createSequence($seq_name)
  654. {
  655. $seqname = $this->getSequenceName($seq_name);
  656. $result = $this->query("CREATE SEQUENCE ${seqname}");
  657. return $result;
  658. }
  659. // }}}
  660. // {{{ dropSequence()
  661. /**
  662. * Deletes a sequence
  663. *
  664. * @param string $seq_name name of the sequence to be deleted
  665. *
  666. * @return int DB_OK on success. A DB_Error object on failure.
  667. *
  668. * @see DB_common::dropSequence(), DB_common::getSequenceName(),
  669. * DB_pgsql::nextID(), DB_pgsql::createSequence()
  670. */
  671. public function dropSequence($seq_name)
  672. {
  673. return $this->query('DROP SEQUENCE '
  674. . $this->getSequenceName($seq_name));
  675. }
  676. // }}}
  677. // {{{ modifyLimitQuery()
  678. /**
  679. * Adds LIMIT clauses to a query string according to current DBMS standards
  680. *
  681. * @param string $query the query to modify
  682. * @param int $from the row to start to fetching (0 = the first row)
  683. * @param int $count the numbers of rows to fetch
  684. * @param mixed $params array, string or numeric data to be used in
  685. * execution of the statement. Quantity of items
  686. * passed must match quantity of placeholders in
  687. * query: meaning 1 placeholder for non-array
  688. * parameters or 1 placeholder per array element.
  689. *
  690. * @return string the query string with LIMIT clauses added
  691. *
  692. * @access protected
  693. */
  694. public function modifyLimitQuery($query, $from, $count, $params = array())
  695. {
  696. return "$query LIMIT $count OFFSET $from";
  697. }
  698. // }}}
  699. // {{{ pgsqlRaiseError()
  700. /**
  701. * Produces a DB_Error object regarding the current problem
  702. *
  703. * @param int $errno if the error is being manually raised pass a
  704. * DB_ERROR* constant here. If this isn't passed
  705. * the error information gathered from the DBMS.
  706. *
  707. * @return object the DB_Error object
  708. *
  709. * @see DB_common::raiseError(),
  710. * DB_pgsql::errorNative(), DB_pgsql::errorCode()
  711. */
  712. public function pgsqlRaiseError($errno = null)
  713. {
  714. $native = $this->errorNative();
  715. if (!$native) {
  716. $native = 'Database connection has been lost.';
  717. $errno = DB_ERROR_CONNECT_FAILED;
  718. }
  719. if ($errno === null) {
  720. $errno = $this->errorCode($native);
  721. }
  722. return $this->raiseError($errno, null, null, null, $native);
  723. }
  724. // }}}
  725. // {{{ errorNative()
  726. /**
  727. * Gets the DBMS' native error message produced by the last query
  728. *
  729. * {@internal Error messages are used instead of error codes
  730. * in order to support older versions of PostgreSQL.}}
  731. *
  732. * @return string the DBMS' error message
  733. */
  734. public function errorNative()
  735. {
  736. return @pg_errormessage($this->connection);
  737. }
  738. // }}}
  739. // {{{ errorCode()
  740. /**
  741. * Determines PEAR::DB error code from the database's text error message.
  742. *
  743. * @param string $errormsg error message returned from the database
  744. * @return integer an error number from a DB error constant
  745. */
  746. public function errorCode($errormsg)
  747. {
  748. static $error_regexps;
  749. if (!isset($error_regexps)) {
  750. $error_regexps = array(
  751. '/column .* (of relation .*)?does not exist/i'
  752. => DB_ERROR_NOSUCHFIELD,
  753. '/(relation|sequence|table).*does not exist|class .* not found/i'
  754. => DB_ERROR_NOSUCHTABLE,
  755. '/index .* does not exist/'
  756. => DB_ERROR_NOT_FOUND,
  757. '/relation .* already exists/i'
  758. => DB_ERROR_ALREADY_EXISTS,
  759. '/(divide|division) by zero$/i'
  760. => DB_ERROR_DIVZERO,
  761. '/pg_atoi: error in .*: can\'t parse /i'
  762. => DB_ERROR_INVALID_NUMBER,
  763. '/invalid input syntax for( type)? (integer|numeric)/i'
  764. => DB_ERROR_INVALID_NUMBER,
  765. '/value .* is out of range for type \w*int/i'
  766. => DB_ERROR_INVALID_NUMBER,
  767. '/integer out of range/i'
  768. => DB_ERROR_INVALID_NUMBER,
  769. '/value too long for type character/i'
  770. => DB_ERROR_INVALID,
  771. '/attribute .* not found|relation .* does not have attribute/i'
  772. => DB_ERROR_NOSUCHFIELD,
  773. '/column .* specified in USING clause does not exist in (left|right) table/i'
  774. => DB_ERROR_NOSUCHFIELD,
  775. '/parser: parse error at or near/i'
  776. => DB_ERROR_SYNTAX,
  777. '/syntax error at/'
  778. => DB_ERROR_SYNTAX,
  779. '/column reference .* is ambiguous/i'
  780. => DB_ERROR_SYNTAX,
  781. '/permission denied/'
  782. => DB_ERROR_ACCESS_VIOLATION,
  783. '/violates not-null constraint/'
  784. => DB_ERROR_CONSTRAINT_NOT_NULL,
  785. '/violates [\w ]+ constraint/'
  786. => DB_ERROR_CONSTRAINT,
  787. '/referential integrity violation/'
  788. => DB_ERROR_CONSTRAINT,
  789. '/more expressions than target columns/i'
  790. => DB_ERROR_VALUE_COUNT_ON_ROW,
  791. );
  792. }
  793. foreach ($error_regexps as $regexp => $code) {
  794. if (preg_match($regexp, $errormsg)) {
  795. return $code;
  796. }
  797. }
  798. // Fall back to DB_ERROR if there was no mapping.
  799. return DB_ERROR;
  800. }
  801. // }}}
  802. // {{{ tableInfo()
  803. /**
  804. * Returns information about a table or a result set
  805. *
  806. * NOTE: only supports 'table' and 'flags' if <var>$result</var>
  807. * is a table name.
  808. *
  809. * @param object|string $result DB_result object from a query or a
  810. * string containing the name of a table.
  811. * While this also accepts a query result
  812. * resource identifier, this behavior is
  813. * deprecated.
  814. * @param int $mode a valid tableInfo mode
  815. *
  816. * @return array an associative array with the information requested.
  817. * A DB_Error object on failure.
  818. *
  819. * @see DB_common::tableInfo()
  820. */
  821. public function tableInfo($result, $mode = null)
  822. {
  823. if (is_string($result)) {
  824. /*
  825. * Probably received a table name.
  826. * Create a result resource identifier.
  827. */
  828. $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0");
  829. $got_string = true;
  830. } elseif (isset($result->result)) {
  831. /*
  832. * Probably received a result object.
  833. * Extract the result resource identifier.
  834. */
  835. $id = $result->result;
  836. $got_string = false;
  837. } else {
  838. /*
  839. * Probably received a result resource identifier.
  840. * Copy it.
  841. * Deprecated. Here for compatibility only.
  842. */
  843. $id = $result;
  844. $got_string = false;
  845. }
  846. if (!is_resource($id)) {
  847. return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
  848. }
  849. if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
  850. $case_func = 'strtolower';
  851. } else {
  852. $case_func = 'strval';
  853. }
  854. $count = @pg_numfields($id);
  855. $res = array();
  856. if ($mode) {
  857. $res['num_fields'] = $count;
  858. }
  859. for ($i = 0; $i < $count; $i++) {
  860. $res[$i] = array(
  861. 'table' => $got_string ? $case_func($result) : '',
  862. 'name' => $case_func(@pg_fieldname($id, $i)),
  863. 'type' => @pg_fieldtype($id, $i),
  864. 'len' => @pg_fieldsize($id, $i),
  865. 'flags' => $got_string
  866. ? $this->_pgFieldFlags($id, $i, $result)
  867. : '',
  868. );
  869. if ($mode & DB_TABLEINFO_ORDER) {
  870. $res['order'][$res[$i]['name']] = $i;
  871. }
  872. if ($mode & DB_TABLEINFO_ORDERTABLE) {
  873. $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
  874. }
  875. }
  876. // free the result only if we were called on a table
  877. if ($got_string) {
  878. @pg_freeresult($id);
  879. }
  880. return $res;
  881. }
  882. // }}}
  883. // {{{ _pgFieldFlags()
  884. /**
  885. * Get a column's flags
  886. *
  887. * Supports "not_null", "default_value", "primary_key", "unique_key"
  888. * and "multiple_key". The default value is passed through
  889. * rawurlencode() in case there are spaces in it.
  890. *
  891. * @param int $resource the PostgreSQL result identifier
  892. * @param int $num_field the field number
  893. *
  894. * @return string the flags
  895. *
  896. * @access private
  897. */
  898. public function _pgFieldFlags($resource, $num_field, $table_name)
  899. {
  900. $field_name = @pg_fieldname($resource, $num_field);
  901. // Check if there's a schema in $table_name and update things
  902. // accordingly.
  903. $from = 'pg_attribute f, pg_class tab, pg_type typ';
  904. if (strpos($table_name, '.') !== false) {
  905. $from .= ', pg_namespace nsp';
  906. list($schema, $table) = explode('.', $table_name);
  907. $tableWhere = "tab.relname = '$table' AND tab.relnamespace = nsp.oid AND nsp.nspname = '$schema'";
  908. } else {
  909. $tableWhere = "tab.relname = '$table_name'";
  910. }
  911. $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef
  912. FROM $from
  913. WHERE tab.relname = typ.typname
  914. AND typ.typrelid = f.attrelid
  915. AND f.attname = '$field_name'
  916. AND $tableWhere");
  917. if (@pg_numrows($result) > 0) {
  918. $row = @pg_fetch_row($result, 0);
  919. $flags = ($row[0] == 't') ? 'not_null ' : '';
  920. if ($row[1] == 't') {
  921. $result = @pg_exec($this->connection, "SELECT a.adsrc
  922. FROM $from, pg_attrdef a
  923. WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
  924. AND f.attrelid = a.adrelid AND f.attname = '$field_name'
  925. AND $tableWhere AND f.attnum = a.adnum");
  926. $row = @pg_fetch_row($result, 0);
  927. $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]);
  928. $flags .= 'default_' . rawurlencode($num) . ' ';
  929. }
  930. } else {
  931. $flags = '';
  932. }
  933. $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
  934. FROM $from, pg_index i
  935. WHERE tab.relname = typ.typname
  936. AND typ.typrelid = f.attrelid
  937. AND f.attrelid = i.indrelid
  938. AND f.attname = '$field_name'
  939. AND $tableWhere");
  940. $count = @pg_numrows($result);
  941. for ($i = 0; $i < $count ; $i++) {
  942. $row = @pg_fetch_row($result, $i);
  943. $keys = explode(' ', $row[2]);
  944. if (in_array($num_field + 1, $keys)) {
  945. $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : '';
  946. $flags .= ($row[1] == 't') ? 'primary_key ' : '';
  947. if (count($keys) > 1) {
  948. $flags .= 'multiple_key ';
  949. }
  950. }
  951. }
  952. return trim($flags);
  953. }
  954. // }}}
  955. // {{{ getSpecialQuery()
  956. /**
  957. * Obtains the query string needed for listing a given type of objects
  958. *
  959. * @param string $type the kind of objects you want to retrieve
  960. *
  961. * @return string the SQL query string or null if the driver doesn't
  962. * support the object type requested
  963. *
  964. * @access protected
  965. * @see DB_common::getListOf()
  966. */
  967. public function getSpecialQuery($type)
  968. {
  969. switch ($type) {
  970. case 'tables':
  971. return 'SELECT c.relname AS "Name"'
  972. . ' FROM pg_class c, pg_user u'
  973. . ' WHERE c.relowner = u.usesysid'
  974. . " AND c.relkind = 'r'"
  975. . ' AND NOT EXISTS'
  976. . ' (SELECT 1 FROM pg_views'
  977. . ' WHERE viewname = c.relname)'
  978. . " AND c.relname !~ '^(pg_|sql_)'"
  979. . ' UNION'
  980. . ' SELECT c.relname AS "Name"'
  981. . ' FROM pg_class c'
  982. . " WHERE c.relkind = 'r'"
  983. . ' AND NOT EXISTS'
  984. . ' (SELECT 1 FROM pg_views'
  985. . ' WHERE viewname = c.relname)'
  986. . ' AND NOT EXISTS'
  987. . ' (SELECT 1 FROM pg_user'
  988. . ' WHERE usesysid = c.relowner)'
  989. . " AND c.relname !~ '^pg_'";
  990. case 'schema.tables':
  991. return "SELECT schemaname || '.' || tablename"
  992. . ' AS "Name"'
  993. . ' FROM pg_catalog.pg_tables'
  994. . ' WHERE schemaname NOT IN'
  995. . " ('pg_catalog', 'information_schema', 'pg_toast')";
  996. case 'schema.views':
  997. return "SELECT schemaname || '.' || viewname from pg_views WHERE schemaname"
  998. . " NOT IN ('information_schema', 'pg_catalog')";
  999. case 'views':
  1000. // Table cols: viewname | viewowner | definition
  1001. return 'SELECT viewname from pg_views WHERE schemaname'
  1002. . " NOT IN ('information_schema', 'pg_catalog')";
  1003. case 'users':
  1004. // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil
  1005. return 'SELECT usename FROM pg_user';
  1006. case 'databases':
  1007. return 'SELECT datname FROM pg_database';
  1008. case 'functions':
  1009. case 'procedures':
  1010. return 'SELECT proname FROM pg_proc WHERE proowner <> 1';
  1011. default:
  1012. return null;
  1013. }
  1014. }
  1015. // }}}
  1016. // {{{ _checkManip()
  1017. /**
  1018. * Checks if the given query is a manipulation query. This also takes into
  1019. * account the _next_query_manip flag and sets the _last_query_manip flag
  1020. * (and resets _next_query_manip) according to the result.
  1021. *
  1022. * @param string The query to check.
  1023. *
  1024. * @return boolean true if the query is a manipulation query, false
  1025. * otherwise
  1026. *
  1027. * @access protected
  1028. */
  1029. public function _checkManip($query)
  1030. {
  1031. return (preg_match('/^\s*(SAVEPOINT|RELEASE)\s+/i', $query)
  1032. || parent::_checkManip($query));
  1033. }
  1034. }
  1035. /*
  1036. * Local variables:
  1037. * tab-width: 4
  1038. * c-basic-offset: 4
  1039. * End:
  1040. */