pgsql.php 36 KB

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