pgsql.php 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | PHP versions 4 and 5 |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox, |
  6. // | Stig. S. Bakken, Lukas Smith |
  7. // | All rights reserved. |
  8. // +----------------------------------------------------------------------+
  9. // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB |
  10. // | API as well as database abstraction for PHP applications. |
  11. // | This LICENSE is in the BSD license style. |
  12. // | |
  13. // | Redistribution and use in source and binary forms, with or without |
  14. // | modification, are permitted provided that the following conditions |
  15. // | are met: |
  16. // | |
  17. // | Redistributions of source code must retain the above copyright |
  18. // | notice, this list of conditions and the following disclaimer. |
  19. // | |
  20. // | Redistributions in binary form must reproduce the above copyright |
  21. // | notice, this list of conditions and the following disclaimer in the |
  22. // | documentation and/or other materials provided with the distribution. |
  23. // | |
  24. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, |
  25. // | Lukas Smith nor the names of his contributors may be used to endorse |
  26. // | or promote products derived from this software without specific prior|
  27. // | written permission. |
  28. // | |
  29. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
  30. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
  31. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
  32. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
  33. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
  34. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  35. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  36. // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
  37. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  38. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  39. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
  40. // | POSSIBILITY OF SUCH DAMAGE. |
  41. // +----------------------------------------------------------------------+
  42. // | Author: Paul Cooper <pgc@ucecom.com> |
  43. // +----------------------------------------------------------------------+
  44. //
  45. // $Id$
  46. require_once 'MDB2/Driver/Manager/Common.php';
  47. /**
  48. * MDB2 MySQL driver for the management modules
  49. *
  50. * @package MDB2
  51. * @category Database
  52. * @author Paul Cooper <pgc@ucecom.com>
  53. */
  54. class MDB2_Driver_Manager_pgsql extends MDB2_Driver_Manager_Common
  55. {
  56. // {{{ createDatabase()
  57. /**
  58. * create a new database
  59. *
  60. * @param string $name name of the database that should be created
  61. * @param array $options array with charset info
  62. *
  63. * @return mixed MDB2_OK on success, a MDB2 error on failure
  64. * @access public
  65. */
  66. function createDatabase($name, $options = array())
  67. {
  68. $db = $this->getDBInstance();
  69. if (MDB2::isError($db)) {
  70. return $db;
  71. }
  72. $name = $db->quoteIdentifier($name, true);
  73. $query = 'CREATE DATABASE ' . $name;
  74. if (!empty($options['charset'])) {
  75. $query .= ' WITH ENCODING ' . $db->quote($options['charset'], 'text');
  76. }
  77. return $db->standaloneQuery($query, null, true);
  78. }
  79. // }}}
  80. // {{{ alterDatabase()
  81. /**
  82. * alter an existing database
  83. *
  84. * @param string $name name of the database that is intended to be changed
  85. * @param array $options array with name, owner info
  86. *
  87. * @return mixed MDB2_OK on success, a MDB2 error on failure
  88. * @access public
  89. */
  90. function alterDatabase($name, $options = array())
  91. {
  92. $db = $this->getDBInstance();
  93. if (MDB2::isError($db)) {
  94. return $db;
  95. }
  96. $query = '';
  97. if (!empty($options['name'])) {
  98. $query .= ' RENAME TO ' . $options['name'];
  99. }
  100. if (!empty($options['owner'])) {
  101. $query .= ' OWNER TO ' . $options['owner'];
  102. }
  103. if (empty($query)) {
  104. return MDB2_OK;
  105. }
  106. $query = 'ALTER DATABASE '. $db->quoteIdentifier($name, true) . $query;
  107. return $db->standaloneQuery($query, null, true);
  108. }
  109. // }}}
  110. // {{{ dropDatabase()
  111. /**
  112. * drop an existing database
  113. *
  114. * @param string $name name of the database that should be dropped
  115. * @return mixed MDB2_OK on success, a MDB2 error on failure
  116. * @access public
  117. */
  118. function dropDatabase($name)
  119. {
  120. $db = $this->getDBInstance();
  121. if (MDB2::isError($db)) {
  122. return $db;
  123. }
  124. $name = $db->quoteIdentifier($name, true);
  125. $query = "DROP DATABASE $name";
  126. return $db->standaloneQuery($query, null, true);
  127. }
  128. // }}}
  129. // {{{ _getAdvancedFKOptions()
  130. /**
  131. * Return the FOREIGN KEY query section dealing with non-standard options
  132. * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
  133. *
  134. * @param array $definition
  135. * @return string
  136. * @access protected
  137. */
  138. function _getAdvancedFKOptions($definition)
  139. {
  140. $query = '';
  141. if (!empty($definition['match'])) {
  142. $query .= ' MATCH '.$definition['match'];
  143. }
  144. if (!empty($definition['onupdate'])) {
  145. $query .= ' ON UPDATE '.$definition['onupdate'];
  146. }
  147. if (!empty($definition['ondelete'])) {
  148. $query .= ' ON DELETE '.$definition['ondelete'];
  149. }
  150. if (!empty($definition['deferrable'])) {
  151. $query .= ' DEFERRABLE';
  152. } else {
  153. $query .= ' NOT DEFERRABLE';
  154. }
  155. if (!empty($definition['initiallydeferred'])) {
  156. $query .= ' INITIALLY DEFERRED';
  157. } else {
  158. $query .= ' INITIALLY IMMEDIATE';
  159. }
  160. return $query;
  161. }
  162. // }}}
  163. // {{{ truncateTable()
  164. /**
  165. * Truncate an existing table (if the TRUNCATE TABLE syntax is not supported,
  166. * it falls back to a DELETE FROM TABLE query)
  167. *
  168. * @param string $name name of the table that should be truncated
  169. * @return mixed MDB2_OK on success, a MDB2 error on failure
  170. * @access public
  171. */
  172. function truncateTable($name)
  173. {
  174. $db = $this->getDBInstance();
  175. if (MDB2::isError($db)) {
  176. return $db;
  177. }
  178. $name = $db->quoteIdentifier($name, true);
  179. $result = $db->exec("TRUNCATE TABLE $name");
  180. if (MDB2::isError($result)) {
  181. return $result;
  182. }
  183. return MDB2_OK;
  184. }
  185. // }}}
  186. // {{{ vacuum()
  187. /**
  188. * Optimize (vacuum) all the tables in the db (or only the specified table)
  189. * and optionally run ANALYZE.
  190. *
  191. * @param string $table table name (all the tables if empty)
  192. * @param array $options an array with driver-specific options:
  193. * - timeout [int] (in seconds) [mssql-only]
  194. * - analyze [boolean] [pgsql and mysql]
  195. * - full [boolean] [pgsql-only]
  196. * - freeze [boolean] [pgsql-only]
  197. *
  198. * @return mixed MDB2_OK success, a MDB2 error on failure
  199. * @access public
  200. */
  201. function vacuum($table = null, $options = array())
  202. {
  203. $db = $this->getDBInstance();
  204. if (MDB2::isError($db)) {
  205. return $db;
  206. }
  207. $query = 'VACUUM';
  208. if (!empty($options['full'])) {
  209. $query .= ' FULL';
  210. }
  211. if (!empty($options['freeze'])) {
  212. $query .= ' FREEZE';
  213. }
  214. if (!empty($options['analyze'])) {
  215. $query .= ' ANALYZE';
  216. }
  217. if (!empty($table)) {
  218. $query .= ' '.$db->quoteIdentifier($table, true);
  219. }
  220. $result = $db->exec($query);
  221. if (MDB2::isError($result)) {
  222. return $result;
  223. }
  224. return MDB2_OK;
  225. }
  226. // }}}
  227. // {{{ alterTable()
  228. /**
  229. * alter an existing table
  230. *
  231. * @param string $name name of the table that is intended to be changed.
  232. * @param array $changes associative array that contains the details of each type
  233. * of change that is intended to be performed. The types of
  234. * changes that are currently supported are defined as follows:
  235. *
  236. * name
  237. *
  238. * New name for the table.
  239. *
  240. * add
  241. *
  242. * Associative array with the names of fields to be added as
  243. * indexes of the array. The value of each entry of the array
  244. * should be set to another associative array with the properties
  245. * of the fields to be added. The properties of the fields should
  246. * be the same as defined by the MDB2 parser.
  247. *
  248. *
  249. * remove
  250. *
  251. * Associative array with the names of fields to be removed as indexes
  252. * of the array. Currently the values assigned to each entry are ignored.
  253. * An empty array should be used for future compatibility.
  254. *
  255. * rename
  256. *
  257. * Associative array with the names of fields to be renamed as indexes
  258. * of the array. The value of each entry of the array should be set to
  259. * another associative array with the entry named name with the new
  260. * field name and the entry named Declaration that is expected to contain
  261. * the portion of the field declaration already in DBMS specific SQL code
  262. * as it is used in the CREATE TABLE statement.
  263. *
  264. * change
  265. *
  266. * Associative array with the names of the fields to be changed as indexes
  267. * of the array. Keep in mind that if it is intended to change either the
  268. * name of a field and any other properties, the change array entries
  269. * should have the new names of the fields as array indexes.
  270. *
  271. * The value of each entry of the array should be set to another associative
  272. * array with the properties of the fields to that are meant to be changed as
  273. * array entries. These entries should be assigned to the new values of the
  274. * respective properties. The properties of the fields should be the same
  275. * as defined by the MDB2 parser.
  276. *
  277. * Example
  278. * array(
  279. * 'name' => 'userlist',
  280. * 'add' => array(
  281. * 'quota' => array(
  282. * 'type' => 'integer',
  283. * 'unsigned' => 1
  284. * )
  285. * ),
  286. * 'remove' => array(
  287. * 'file_limit' => array(),
  288. * 'time_limit' => array()
  289. * ),
  290. * 'change' => array(
  291. * 'name' => array(
  292. * 'length' => '20',
  293. * 'definition' => array(
  294. * 'type' => 'text',
  295. * 'length' => 20,
  296. * ),
  297. * )
  298. * ),
  299. * 'rename' => array(
  300. * 'sex' => array(
  301. * 'name' => 'gender',
  302. * 'definition' => array(
  303. * 'type' => 'text',
  304. * 'length' => 1,
  305. * 'default' => 'M',
  306. * ),
  307. * )
  308. * )
  309. * )
  310. *
  311. * @param boolean $check indicates whether the function should just check if the DBMS driver
  312. * can perform the requested table alterations if the value is true or
  313. * actually perform them otherwise.
  314. * @access public
  315. *
  316. * @return mixed MDB2_OK on success, a MDB2 error on failure
  317. */
  318. function alterTable($name, $changes, $check)
  319. {
  320. $db = $this->getDBInstance();
  321. if (MDB2::isError($db)) {
  322. return $db;
  323. }
  324. foreach ($changes as $change_name => $change) {
  325. switch ($change_name) {
  326. case 'add':
  327. case 'remove':
  328. case 'change':
  329. case 'name':
  330. case 'rename':
  331. break;
  332. default:
  333. return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
  334. 'change type "'.$change_name.'\" not yet supported', __FUNCTION__);
  335. }
  336. }
  337. if ($check) {
  338. return MDB2_OK;
  339. }
  340. $name = $db->quoteIdentifier($name, true);
  341. if (!empty($changes['remove']) && is_array($changes['remove'])) {
  342. foreach ($changes['remove'] as $field_name => $field) {
  343. $field_name = $db->quoteIdentifier($field_name, true);
  344. $query = 'DROP ' . $field_name;
  345. $result = $db->exec("ALTER TABLE $name $query");
  346. if (MDB2::isError($result)) {
  347. return $result;
  348. }
  349. }
  350. }
  351. if (!empty($changes['rename']) && is_array($changes['rename'])) {
  352. foreach ($changes['rename'] as $field_name => $field) {
  353. $field_name = $db->quoteIdentifier($field_name, true);
  354. $result = $db->exec("ALTER TABLE $name RENAME COLUMN $field_name TO ".$db->quoteIdentifier($field['name'], true));
  355. if (MDB2::isError($result)) {
  356. return $result;
  357. }
  358. }
  359. }
  360. if (!empty($changes['add']) && is_array($changes['add'])) {
  361. foreach ($changes['add'] as $field_name => $field) {
  362. $query = 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field);
  363. $result = $db->exec("ALTER TABLE $name $query");
  364. if (MDB2::isError($result)) {
  365. return $result;
  366. }
  367. }
  368. }
  369. if (!empty($changes['change']) && is_array($changes['change'])) {
  370. foreach ($changes['change'] as $field_name => $field) {
  371. $field_name = $db->quoteIdentifier($field_name, true);
  372. if (!empty($field['definition']['type'])) {
  373. $server_info = $db->getServerVersion();
  374. if (MDB2::isError($server_info)) {
  375. return $server_info;
  376. }
  377. if (is_array($server_info) && $server_info['major'] < 8) {
  378. return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
  379. 'changing column type for "'.$change_name.'\" requires PostgreSQL 8.0 or above', __FUNCTION__);
  380. }
  381. $db->loadModule('Datatype', null, true);
  382. $type = $db->datatype->getTypeDeclaration($field['definition']);
  383. $query = "ALTER $field_name TYPE $type USING CAST($field_name AS $type)";
  384. $result = $db->exec("ALTER TABLE $name $query");
  385. if (MDB2::isError($result)) {
  386. return $result;
  387. }
  388. }
  389. if (array_key_exists('default', $field['definition'])) {
  390. $query = "ALTER $field_name SET DEFAULT ".$db->quote($field['definition']['default'], $field['definition']['type']);
  391. $result = $db->exec("ALTER TABLE $name $query");
  392. if (MDB2::isError($result)) {
  393. return $result;
  394. }
  395. }
  396. if (array_key_exists('notnull', $field['definition'])) {
  397. $query = "ALTER $field_name ".($field['definition']['notnull'] ? 'SET' : 'DROP').' NOT NULL';
  398. $result = $db->exec("ALTER TABLE $name $query");
  399. if (MDB2::isError($result)) {
  400. return $result;
  401. }
  402. }
  403. }
  404. }
  405. if (!empty($changes['name'])) {
  406. $change_name = $db->quoteIdentifier($changes['name'], true);
  407. $result = $db->exec("ALTER TABLE $name RENAME TO ".$change_name);
  408. if (MDB2::isError($result)) {
  409. return $result;
  410. }
  411. }
  412. return MDB2_OK;
  413. }
  414. // }}}
  415. // {{{ listDatabases()
  416. /**
  417. * list all databases
  418. *
  419. * @return mixed array of database names on success, a MDB2 error on failure
  420. * @access public
  421. */
  422. function listDatabases()
  423. {
  424. $db = $this->getDBInstance();
  425. if (MDB2::isError($db)) {
  426. return $db;
  427. }
  428. $query = 'SELECT datname FROM pg_database';
  429. $result2 = $db->standaloneQuery($query, array('text'), false);
  430. if (!MDB2::isResultCommon($result2)) {
  431. return $result2;
  432. }
  433. $result = $result2->fetchCol();
  434. $result2->free();
  435. if (MDB2::isError($result)) {
  436. return $result;
  437. }
  438. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  439. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  440. }
  441. return $result;
  442. }
  443. // }}}
  444. // {{{ listUsers()
  445. /**
  446. * list all users
  447. *
  448. * @return mixed array of user names on success, a MDB2 error on failure
  449. * @access public
  450. */
  451. function listUsers()
  452. {
  453. $db = $this->getDBInstance();
  454. if (MDB2::isError($db)) {
  455. return $db;
  456. }
  457. $query = 'SELECT usename FROM pg_user';
  458. $result2 = $db->standaloneQuery($query, array('text'), false);
  459. if (!MDB2::isResultCommon($result2)) {
  460. return $result2;
  461. }
  462. $result = $result2->fetchCol();
  463. $result2->free();
  464. return $result;
  465. }
  466. // }}}
  467. // {{{ listViews()
  468. /**
  469. * list all views in the current database
  470. *
  471. * @return mixed array of view names on success, a MDB2 error on failure
  472. * @access public
  473. */
  474. function listViews()
  475. {
  476. $db = $this->getDBInstance();
  477. if (MDB2::isError($db)) {
  478. return $db;
  479. }
  480. $query = "SELECT viewname
  481. FROM pg_views
  482. WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
  483. AND viewname !~ '^pg_'";
  484. $result = $db->queryCol($query);
  485. if (MDB2::isError($result)) {
  486. return $result;
  487. }
  488. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  489. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  490. }
  491. return $result;
  492. }
  493. // }}}
  494. // {{{ listTableViews()
  495. /**
  496. * list the views in the database that reference a given table
  497. *
  498. * @param string table for which all referenced views should be found
  499. * @return mixed array of view names on success, a MDB2 error on failure
  500. * @access public
  501. */
  502. function listTableViews($table)
  503. {
  504. $db = $this->getDBInstance();
  505. if (MDB2::isError($db)) {
  506. return $db;
  507. }
  508. $query = 'SELECT viewname FROM pg_views NATURAL JOIN pg_tables';
  509. $query.= ' WHERE tablename ='.$db->quote($table, 'text');
  510. $result = $db->queryCol($query);
  511. if (MDB2::isError($result)) {
  512. return $result;
  513. }
  514. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  515. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  516. }
  517. return $result;
  518. }
  519. // }}}
  520. // {{{ listFunctions()
  521. /**
  522. * list all functions in the current database
  523. *
  524. * @return mixed array of function names on success, a MDB2 error on failure
  525. * @access public
  526. */
  527. function listFunctions()
  528. {
  529. $db = $this->getDBInstance();
  530. if (MDB2::isError($db)) {
  531. return $db;
  532. }
  533. $query = "
  534. SELECT
  535. proname
  536. FROM
  537. pg_proc pr,
  538. pg_type tp
  539. WHERE
  540. tp.oid = pr.prorettype
  541. AND pr.proisagg = FALSE
  542. AND tp.typname <> 'trigger'
  543. AND pr.pronamespace IN
  544. (SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')";
  545. $result = $db->queryCol($query);
  546. if (MDB2::isError($result)) {
  547. return $result;
  548. }
  549. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  550. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  551. }
  552. return $result;
  553. }
  554. // }}}
  555. // {{{ listTableTriggers()
  556. /**
  557. * list all triggers in the database that reference a given table
  558. *
  559. * @param string table for which all referenced triggers should be found
  560. * @return mixed array of trigger names on success, a MDB2 error on failure
  561. * @access public
  562. */
  563. function listTableTriggers($table = null)
  564. {
  565. $db = $this->getDBInstance();
  566. if (MDB2::isError($db)) {
  567. return $db;
  568. }
  569. $query = 'SELECT trg.tgname AS trigger_name
  570. FROM pg_trigger trg,
  571. pg_class tbl
  572. WHERE trg.tgrelid = tbl.oid';
  573. if (null !== $table) {
  574. $table = $db->quote(strtoupper($table), 'text');
  575. $query .= " AND UPPER(tbl.relname) = $table";
  576. }
  577. $result = $db->queryCol($query);
  578. if (MDB2::isError($result)) {
  579. return $result;
  580. }
  581. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  582. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  583. }
  584. return $result;
  585. }
  586. // }}}
  587. // {{{ listTables()
  588. /**
  589. * list all tables in the current database
  590. *
  591. * @return mixed array of table names on success, a MDB2 error on failure
  592. * @access public
  593. */
  594. function listTables()
  595. {
  596. $db = $this->getDBInstance();
  597. if (MDB2::isError($db)) {
  598. return $db;
  599. }
  600. // gratuitously stolen from PEAR DB _getSpecialQuery in pgsql.php
  601. $query = 'SELECT c.relname AS "Name"'
  602. . ' FROM pg_class c, pg_user u'
  603. . ' WHERE c.relowner = u.usesysid'
  604. . " AND c.relkind = 'r'"
  605. . ' AND NOT EXISTS'
  606. . ' (SELECT 1 FROM pg_views'
  607. . ' WHERE viewname = c.relname)'
  608. . " AND c.relname !~ '^(pg_|sql_)'"
  609. . ' UNION'
  610. . ' SELECT c.relname AS "Name"'
  611. . ' FROM pg_class c'
  612. . " WHERE c.relkind = 'r'"
  613. . ' AND NOT EXISTS'
  614. . ' (SELECT 1 FROM pg_views'
  615. . ' WHERE viewname = c.relname)'
  616. . ' AND NOT EXISTS'
  617. . ' (SELECT 1 FROM pg_user'
  618. . ' WHERE usesysid = c.relowner)'
  619. . " AND c.relname !~ '^pg_'";
  620. $result = $db->queryCol($query);
  621. if (MDB2::isError($result)) {
  622. return $result;
  623. }
  624. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  625. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  626. }
  627. return $result;
  628. }
  629. // }}}
  630. // {{{ listTableFields()
  631. /**
  632. * list all fields in a table in the current database
  633. *
  634. * @param string $table name of table that should be used in method
  635. * @return mixed array of field names on success, a MDB2 error on failure
  636. * @access public
  637. */
  638. function listTableFields($table)
  639. {
  640. $db = $this->getDBInstance();
  641. if (MDB2::isError($db)) {
  642. return $db;
  643. }
  644. list($schema, $table) = $this->splitTableSchema($table);
  645. $table = $db->quoteIdentifier($table, true);
  646. if (!empty($schema)) {
  647. $table = $db->quoteIdentifier($schema, true) . '.' .$table;
  648. }
  649. $db->setLimit(1);
  650. $result2 = $db->query("SELECT * FROM $table");
  651. if (MDB2::isError($result2)) {
  652. return $result2;
  653. }
  654. $result = $result2->getColumnNames();
  655. $result2->free();
  656. if (MDB2::isError($result)) {
  657. return $result;
  658. }
  659. return array_flip($result);
  660. }
  661. // }}}
  662. // {{{ listTableIndexes()
  663. /**
  664. * list all indexes in a table
  665. *
  666. * @param string $table name of table that should be used in method
  667. * @return mixed array of index names on success, a MDB2 error on failure
  668. * @access public
  669. */
  670. function listTableIndexes($table)
  671. {
  672. $db = $this->getDBInstance();
  673. if (MDB2::isError($db)) {
  674. return $db;
  675. }
  676. list($schema, $table) = $this->splitTableSchema($table);
  677. $table = $db->quote($table, 'text');
  678. $subquery = "SELECT indexrelid
  679. FROM pg_index
  680. LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid
  681. LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
  682. WHERE pg_class.relname = $table
  683. AND indisunique != 't'
  684. AND indisprimary != 't'";
  685. if (!empty($schema)) {
  686. $subquery .= ' AND pg_namespace.nspname = '.$db->quote($schema, 'text');
  687. }
  688. $query = "SELECT relname FROM pg_class WHERE oid IN ($subquery)";
  689. $indexes = $db->queryCol($query, 'text');
  690. if (MDB2::isError($indexes)) {
  691. return $indexes;
  692. }
  693. $result = array();
  694. foreach ($indexes as $index) {
  695. $index = $this->_fixIndexName($index);
  696. if (!empty($index)) {
  697. $result[$index] = true;
  698. }
  699. }
  700. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  701. $result = array_change_key_case($result, $db->options['field_case']);
  702. }
  703. return array_keys($result);
  704. }
  705. // }}}
  706. // {{{ dropConstraint()
  707. /**
  708. * drop existing constraint
  709. *
  710. * @param string $table name of table that should be used in method
  711. * @param string $name name of the constraint to be dropped
  712. * @param string $primary hint if the constraint is primary
  713. *
  714. * @return mixed MDB2_OK on success, a MDB2 error on failure
  715. * @access public
  716. */
  717. function dropConstraint($table, $name, $primary = false)
  718. {
  719. $db = $this->getDBInstance();
  720. if (MDB2::isError($db)) {
  721. return $db;
  722. }
  723. // is it an UNIQUE index?
  724. $query = 'SELECT relname
  725. FROM pg_class
  726. WHERE oid IN (
  727. SELECT indexrelid
  728. FROM pg_index, pg_class
  729. WHERE pg_class.relname = '.$db->quote($table, 'text').'
  730. AND pg_class.oid = pg_index.indrelid
  731. AND indisunique = \'t\')
  732. EXCEPT
  733. SELECT conname
  734. FROM pg_constraint, pg_class
  735. WHERE pg_constraint.conrelid = pg_class.oid
  736. AND relname = '. $db->quote($table, 'text');
  737. $unique = $db->queryCol($query, 'text');
  738. if (MDB2::isError($unique) || empty($unique)) {
  739. // not an UNIQUE index, maybe a CONSTRAINT
  740. return parent::dropConstraint($table, $name, $primary);
  741. }
  742. if (in_array($name, $unique)) {
  743. $result = $db->exec('DROP INDEX '.$db->quoteIdentifier($name, true));
  744. if (MDB2::isError($result)) {
  745. return $result;
  746. }
  747. return MDB2_OK;
  748. }
  749. $idxname = $db->getIndexName($name);
  750. if (in_array($idxname, $unique)) {
  751. $result = $db->exec('DROP INDEX '.$db->quoteIdentifier($idxname, true));
  752. if (MDB2::isError($result)) {
  753. return $result;
  754. }
  755. return MDB2_OK;
  756. }
  757. return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  758. $name . ' is not an existing constraint for table ' . $table, __FUNCTION__);
  759. }
  760. // }}}
  761. // {{{ listTableConstraints()
  762. /**
  763. * list all constraints in a table
  764. *
  765. * @param string $table name of table that should be used in method
  766. * @return mixed array of constraint names on success, a MDB2 error on failure
  767. * @access public
  768. */
  769. function listTableConstraints($table)
  770. {
  771. $db = $this->getDBInstance();
  772. if (MDB2::isError($db)) {
  773. return $db;
  774. }
  775. list($schema, $table) = $this->splitTableSchema($table);
  776. $table = $db->quote($table, 'text');
  777. $query = 'SELECT conname
  778. FROM pg_constraint
  779. LEFT JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
  780. LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
  781. WHERE relname = ' .$table;
  782. if (!empty($schema)) {
  783. $query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text');
  784. }
  785. $query .= '
  786. UNION DISTINCT
  787. SELECT relname
  788. FROM pg_class
  789. WHERE oid IN (
  790. SELECT indexrelid
  791. FROM pg_index
  792. LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid
  793. LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
  794. WHERE pg_class.relname = '.$table.'
  795. AND indisunique = \'t\'';
  796. if (!empty($schema)) {
  797. $query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text');
  798. }
  799. $query .= ')';
  800. $constraints = $db->queryCol($query);
  801. if (MDB2::isError($constraints)) {
  802. return $constraints;
  803. }
  804. $result = array();
  805. foreach ($constraints as $constraint) {
  806. $constraint = $this->_fixIndexName($constraint);
  807. if (!empty($constraint)) {
  808. $result[$constraint] = true;
  809. }
  810. }
  811. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
  812. && $db->options['field_case'] == CASE_LOWER
  813. ) {
  814. $result = array_change_key_case($result, $db->options['field_case']);
  815. }
  816. return array_keys($result);
  817. }
  818. // }}}
  819. // {{{ createSequence()
  820. /**
  821. * create sequence
  822. *
  823. * @param string $seq_name name of the sequence to be created
  824. * @param string $start start value of the sequence; default is 1
  825. * @return mixed MDB2_OK on success, a MDB2 error on failure
  826. * @access public
  827. */
  828. function createSequence($seq_name, $start = 1)
  829. {
  830. $db = $this->getDBInstance();
  831. if (MDB2::isError($db)) {
  832. return $db;
  833. }
  834. $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
  835. $result = $db->exec("CREATE SEQUENCE $sequence_name INCREMENT 1".
  836. ($start < 1 ? " MINVALUE $start" : '')." START $start");
  837. if (MDB2::isError($result)) {
  838. return $result;
  839. }
  840. return MDB2_OK;
  841. }
  842. // }}}
  843. // {{{ dropSequence()
  844. /**
  845. * drop existing sequence
  846. *
  847. * @param string $seq_name name of the sequence to be dropped
  848. * @return mixed MDB2_OK on success, a MDB2 error on failure
  849. * @access public
  850. */
  851. function dropSequence($seq_name)
  852. {
  853. $db = $this->getDBInstance();
  854. if (MDB2::isError($db)) {
  855. return $db;
  856. }
  857. $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
  858. $result = $db->exec("DROP SEQUENCE $sequence_name");
  859. if (MDB2::isError($result)) {
  860. return $result;
  861. }
  862. return MDB2_OK;
  863. }
  864. // }}}
  865. // {{{ listSequences()
  866. /**
  867. * list all sequences in the current database
  868. *
  869. * @return mixed array of sequence names on success, a MDB2 error on failure
  870. * @access public
  871. */
  872. function listSequences()
  873. {
  874. $db = $this->getDBInstance();
  875. if (MDB2::isError($db)) {
  876. return $db;
  877. }
  878. $query = "SELECT relname FROM pg_class WHERE relkind = 'S' AND relnamespace IN";
  879. $query.= "(SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')";
  880. $table_names = $db->queryCol($query);
  881. if (MDB2::isError($table_names)) {
  882. return $table_names;
  883. }
  884. $result = array();
  885. foreach ($table_names as $table_name) {
  886. $result[] = $this->_fixSequenceName($table_name);
  887. }
  888. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  889. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  890. }
  891. return $result;
  892. }
  893. }
  894. ?>