mysqli.php 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472
  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: Lukas Smith <smith@pooteeweet.org> |
  43. // +----------------------------------------------------------------------+
  44. //
  45. // $Id$
  46. //
  47. require_once 'MDB2/Driver/Manager/Common.php';
  48. /**
  49. * MDB2 MySQLi driver for the management modules
  50. *
  51. * @package MDB2
  52. * @category Database
  53. * @author Lukas Smith <smith@pooteeweet.org>
  54. */
  55. class MDB2_Driver_Manager_mysqli extends MDB2_Driver_Manager_Common
  56. {
  57. // }}}
  58. // {{{ createDatabase()
  59. /**
  60. * create a new database
  61. *
  62. * @param string $name name of the database that should be created
  63. * @param array $options array with charset, collation info
  64. *
  65. * @return mixed MDB2_OK on success, a MDB2 error on failure
  66. * @access public
  67. */
  68. function createDatabase($name, $options = array())
  69. {
  70. $db = $this->getDBInstance();
  71. if (MDB2::isError($db)) {
  72. return $db;
  73. }
  74. $name = $db->quoteIdentifier($name, true);
  75. $query = 'CREATE DATABASE ' . $name;
  76. if (!empty($options['charset'])) {
  77. $query .= ' DEFAULT CHARACTER SET ' . $db->quote($options['charset'], 'text');
  78. }
  79. if (!empty($options['collation'])) {
  80. $query .= ' COLLATE ' . $db->quote($options['collation'], 'text');
  81. }
  82. return $db->standaloneQuery($query, null, true);
  83. }
  84. // }}}
  85. // {{{ alterDatabase()
  86. /**
  87. * alter an existing database
  88. *
  89. * @param string $name name of the database that is intended to be changed
  90. * @param array $options array with charset, collation info
  91. *
  92. * @return mixed MDB2_OK on success, a MDB2 error on failure
  93. * @access public
  94. */
  95. function alterDatabase($name, $options = array())
  96. {
  97. $db = $this->getDBInstance();
  98. if (MDB2::isError($db)) {
  99. return $db;
  100. }
  101. $query = 'ALTER DATABASE '. $db->quoteIdentifier($name, true);
  102. if (!empty($options['charset'])) {
  103. $query .= ' DEFAULT CHARACTER SET ' . $db->quote($options['charset'], 'text');
  104. }
  105. if (!empty($options['collation'])) {
  106. $query .= ' COLLATE ' . $db->quote($options['collation'], 'text');
  107. }
  108. return $db->standaloneQuery($query, null, true);
  109. }
  110. // }}}
  111. // {{{ dropDatabase()
  112. /**
  113. * drop an existing database
  114. *
  115. * @param string $name name of the database that should be dropped
  116. * @return mixed MDB2_OK on success, a MDB2 error on failure
  117. * @access public
  118. */
  119. function dropDatabase($name)
  120. {
  121. $db = $this->getDBInstance();
  122. if (MDB2::isError($db)) {
  123. return $db;
  124. }
  125. $name = $db->quoteIdentifier($name, true);
  126. $query = "DROP DATABASE $name";
  127. return $db->standaloneQuery($query, null, true);
  128. }
  129. // }}}
  130. // {{{ _getAdvancedFKOptions()
  131. /**
  132. * Return the FOREIGN KEY query section dealing with non-standard options
  133. * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
  134. *
  135. * @param array $definition
  136. * @return string
  137. * @access protected
  138. */
  139. function _getAdvancedFKOptions($definition)
  140. {
  141. $query = '';
  142. if (!empty($definition['match'])) {
  143. $query .= ' MATCH '.$definition['match'];
  144. }
  145. if (!empty($definition['onupdate'])) {
  146. $query .= ' ON UPDATE '.$definition['onupdate'];
  147. }
  148. if (!empty($definition['ondelete'])) {
  149. $query .= ' ON DELETE '.$definition['ondelete'];
  150. }
  151. return $query;
  152. }
  153. // }}}
  154. // {{{ createTable()
  155. /**
  156. * create a new table
  157. *
  158. * @param string $name Name of the database that should be created
  159. * @param array $fields Associative array that contains the definition of each field of the new table
  160. * The indexes of the array entries are the names of the fields of the table an
  161. * the array entry values are associative arrays like those that are meant to be
  162. * passed with the field definitions to get[Type]Declaration() functions.
  163. * array(
  164. * 'id' => array(
  165. * 'type' => 'integer',
  166. * 'unsigned' => 1
  167. * 'notnull' => 1
  168. * 'default' => 0
  169. * ),
  170. * 'name' => array(
  171. * 'type' => 'text',
  172. * 'length' => 12
  173. * ),
  174. * 'password' => array(
  175. * 'type' => 'text',
  176. * 'length' => 12
  177. * )
  178. * );
  179. * @param array $options An associative array of table options:
  180. * array(
  181. * 'comment' => 'Foo',
  182. * 'charset' => 'utf8',
  183. * 'collate' => 'utf8_unicode_ci',
  184. * 'type' => 'innodb',
  185. * );
  186. *
  187. * @return mixed MDB2_OK on success, a MDB2 error on failure
  188. * @access public
  189. */
  190. function createTable($name, $fields, $options = array())
  191. {
  192. $db = $this->getDBInstance();
  193. if (MDB2::isError($db)) {
  194. return $db;
  195. }
  196. // if we have an AUTO_INCREMENT column and a PK on more than one field,
  197. // we have to handle it differently...
  198. $autoincrement = null;
  199. if (empty($options['primary'])) {
  200. $pk_fields = array();
  201. foreach ($fields as $fieldname => $def) {
  202. if (!empty($def['primary'])) {
  203. $pk_fields[$fieldname] = true;
  204. }
  205. if (!empty($def['autoincrement'])) {
  206. $autoincrement = $fieldname;
  207. }
  208. }
  209. if ((null !== $autoincrement) && count($pk_fields) > 1) {
  210. $options['primary'] = $pk_fields;
  211. } else {
  212. // the PK constraint is on max one field => OK
  213. $autoincrement = null;
  214. }
  215. }
  216. $query = $this->_getCreateTableQuery($name, $fields, $options);
  217. if (MDB2::isError($query)) {
  218. return $query;
  219. }
  220. if (null !== $autoincrement) {
  221. // we have to remove the PK clause added by _getIntegerDeclaration()
  222. $query = str_replace('AUTO_INCREMENT PRIMARY KEY', 'AUTO_INCREMENT', $query);
  223. }
  224. $options_strings = array();
  225. if (!empty($options['comment'])) {
  226. $options_strings['comment'] = 'COMMENT = '.$db->quote($options['comment'], 'text');
  227. }
  228. if (!empty($options['charset'])) {
  229. $options_strings['charset'] = 'DEFAULT CHARACTER SET '.$options['charset'];
  230. if (!empty($options['collate'])) {
  231. $options_strings['charset'].= ' COLLATE '.$options['collate'];
  232. }
  233. }
  234. $type = false;
  235. if (!empty($options['type'])) {
  236. $type = $options['type'];
  237. } elseif ($db->options['default_table_type']) {
  238. $type = $db->options['default_table_type'];
  239. }
  240. if ($type) {
  241. $options_strings[] = "ENGINE = $type";
  242. }
  243. if (!empty($options_strings)) {
  244. $query .= ' '.implode(' ', $options_strings);
  245. }
  246. $result = $db->exec($query);
  247. if (MDB2::isError($result)) {
  248. return $result;
  249. }
  250. return MDB2_OK;
  251. }
  252. // }}}
  253. // {{{ dropTable()
  254. /**
  255. * drop an existing table
  256. *
  257. * @param string $name name of the table that should be dropped
  258. * @return mixed MDB2_OK on success, a MDB2 error on failure
  259. * @access public
  260. */
  261. function dropTable($name)
  262. {
  263. $db = $this->getDBInstance();
  264. if (MDB2::isError($db)) {
  265. return $db;
  266. }
  267. //delete the triggers associated to existing FK constraints
  268. $constraints = $this->listTableConstraints($name);
  269. if (!MDB2::isError($constraints) && !empty($constraints)) {
  270. $db->loadModule('Reverse', null, true);
  271. foreach ($constraints as $constraint) {
  272. $definition = $db->reverse->getTableConstraintDefinition($name, $constraint);
  273. if (!MDB2::isError($definition) && !empty($definition['foreign'])) {
  274. $result = $this->_dropFKTriggers($name, $constraint, $definition['references']['table']);
  275. if (MDB2::isError($result)) {
  276. return $result;
  277. }
  278. }
  279. }
  280. }
  281. return parent::dropTable($name);
  282. }
  283. // }}}
  284. // {{{ truncateTable()
  285. /**
  286. * Truncate an existing table (if the TRUNCATE TABLE syntax is not supported,
  287. * it falls back to a DELETE FROM TABLE query)
  288. *
  289. * @param string $name name of the table that should be truncated
  290. * @return mixed MDB2_OK on success, a MDB2 error on failure
  291. * @access public
  292. */
  293. function truncateTable($name)
  294. {
  295. $db = $this->getDBInstance();
  296. if (MDB2::isError($db)) {
  297. return $db;
  298. }
  299. $name = $db->quoteIdentifier($name, true);
  300. $result = $db->exec("TRUNCATE TABLE $name");
  301. if (MDB2::isError($result)) {
  302. return $result;
  303. }
  304. return MDB2_OK;
  305. }
  306. // }}}
  307. // {{{ vacuum()
  308. /**
  309. * Optimize (vacuum) all the tables in the db (or only the specified table)
  310. * and optionally run ANALYZE.
  311. *
  312. * @param string $table table name (all the tables if empty)
  313. * @param array $options an array with driver-specific options:
  314. * - timeout [int] (in seconds) [mssql-only]
  315. * - analyze [boolean] [pgsql and mysql]
  316. * - full [boolean] [pgsql-only]
  317. * - freeze [boolean] [pgsql-only]
  318. *
  319. * @return mixed MDB2_OK success, a MDB2 error on failure
  320. * @access public
  321. */
  322. function vacuum($table = null, $options = array())
  323. {
  324. $db = $this->getDBInstance();
  325. if (MDB2::isError($db)) {
  326. return $db;
  327. }
  328. if (empty($table)) {
  329. $table = $this->listTables();
  330. if (MDB2::isError($table)) {
  331. return $table;
  332. }
  333. }
  334. if (is_array($table)) {
  335. foreach (array_keys($table) as $k) {
  336. $table[$k] = $db->quoteIdentifier($table[$k], true);
  337. }
  338. $table = implode(', ', $table);
  339. } else {
  340. $table = $db->quoteIdentifier($table, true);
  341. }
  342. $result = $db->exec('OPTIMIZE TABLE '.$table);
  343. if (MDB2::isError($result)) {
  344. return $result;
  345. }
  346. if (!empty($options['analyze'])) {
  347. $result = $db->exec('ANALYZE TABLE '.$table);
  348. if (MDB2::isError($result)) {
  349. return $result;
  350. }
  351. }
  352. return MDB2_OK;
  353. }
  354. // }}}
  355. // {{{ alterTable()
  356. /**
  357. * alter an existing table
  358. *
  359. * @param string $name name of the table that is intended to be changed.
  360. * @param array $changes associative array that contains the details of each type
  361. * of change that is intended to be performed. The types of
  362. * changes that are currently supported are defined as follows:
  363. *
  364. * name
  365. *
  366. * New name for the table.
  367. *
  368. * add
  369. *
  370. * Associative array with the names of fields to be added as
  371. * indexes of the array. The value of each entry of the array
  372. * should be set to another associative array with the properties
  373. * of the fields to be added. The properties of the fields should
  374. * be the same as defined by the MDB2 parser.
  375. *
  376. *
  377. * remove
  378. *
  379. * Associative array with the names of fields to be removed as indexes
  380. * of the array. Currently the values assigned to each entry are ignored.
  381. * An empty array should be used for future compatibility.
  382. *
  383. * rename
  384. *
  385. * Associative array with the names of fields to be renamed as indexes
  386. * of the array. The value of each entry of the array should be set to
  387. * another associative array with the entry named name with the new
  388. * field name and the entry named Declaration that is expected to contain
  389. * the portion of the field declaration already in DBMS specific SQL code
  390. * as it is used in the CREATE TABLE statement.
  391. *
  392. * change
  393. *
  394. * Associative array with the names of the fields to be changed as indexes
  395. * of the array. Keep in mind that if it is intended to change either the
  396. * name of a field and any other properties, the change array entries
  397. * should have the new names of the fields as array indexes.
  398. *
  399. * The value of each entry of the array should be set to another associative
  400. * array with the properties of the fields to that are meant to be changed as
  401. * array entries. These entries should be assigned to the new values of the
  402. * respective properties. The properties of the fields should be the same
  403. * as defined by the MDB2 parser.
  404. *
  405. * Example
  406. * array(
  407. * 'name' => 'userlist',
  408. * 'add' => array(
  409. * 'quota' => array(
  410. * 'type' => 'integer',
  411. * 'unsigned' => 1
  412. * )
  413. * ),
  414. * 'remove' => array(
  415. * 'file_limit' => array(),
  416. * 'time_limit' => array()
  417. * ),
  418. * 'change' => array(
  419. * 'name' => array(
  420. * 'length' => '20',
  421. * 'definition' => array(
  422. * 'type' => 'text',
  423. * 'length' => 20,
  424. * ),
  425. * )
  426. * ),
  427. * 'rename' => array(
  428. * 'sex' => array(
  429. * 'name' => 'gender',
  430. * 'definition' => array(
  431. * 'type' => 'text',
  432. * 'length' => 1,
  433. * 'default' => 'M',
  434. * ),
  435. * )
  436. * )
  437. * )
  438. *
  439. * @param boolean $check indicates whether the function should just check if the DBMS driver
  440. * can perform the requested table alterations if the value is true or
  441. * actually perform them otherwise.
  442. * @access public
  443. *
  444. * @return mixed MDB2_OK on success, a MDB2 error on failure
  445. */
  446. function alterTable($name, $changes, $check)
  447. {
  448. $db = $this->getDBInstance();
  449. if (MDB2::isError($db)) {
  450. return $db;
  451. }
  452. foreach ($changes as $change_name => $change) {
  453. switch ($change_name) {
  454. case 'add':
  455. case 'remove':
  456. case 'change':
  457. case 'rename':
  458. case 'name':
  459. break;
  460. default:
  461. return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
  462. 'change type "'.$change_name.'" not yet supported', __FUNCTION__);
  463. }
  464. }
  465. if ($check) {
  466. return MDB2_OK;
  467. }
  468. $query = '';
  469. if (!empty($changes['name'])) {
  470. $change_name = $db->quoteIdentifier($changes['name'], true);
  471. $query .= 'RENAME TO ' . $change_name;
  472. }
  473. if (!empty($changes['add']) && is_array($changes['add'])) {
  474. foreach ($changes['add'] as $field_name => $field) {
  475. if ($query) {
  476. $query.= ', ';
  477. }
  478. $query.= 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field);
  479. }
  480. }
  481. if (!empty($changes['remove']) && is_array($changes['remove'])) {
  482. foreach ($changes['remove'] as $field_name => $field) {
  483. if ($query) {
  484. $query.= ', ';
  485. }
  486. $field_name = $db->quoteIdentifier($field_name, true);
  487. $query.= 'DROP ' . $field_name;
  488. }
  489. }
  490. $rename = array();
  491. if (!empty($changes['rename']) && is_array($changes['rename'])) {
  492. foreach ($changes['rename'] as $field_name => $field) {
  493. $rename[$field['name']] = $field_name;
  494. }
  495. }
  496. if (!empty($changes['change']) && is_array($changes['change'])) {
  497. foreach ($changes['change'] as $field_name => $field) {
  498. if ($query) {
  499. $query.= ', ';
  500. }
  501. if (isset($rename[$field_name])) {
  502. $old_field_name = $rename[$field_name];
  503. unset($rename[$field_name]);
  504. } else {
  505. $old_field_name = $field_name;
  506. }
  507. $old_field_name = $db->quoteIdentifier($old_field_name, true);
  508. $query.= "CHANGE $old_field_name " . $db->getDeclaration($field['definition']['type'], $field_name, $field['definition']);
  509. }
  510. }
  511. if (!empty($rename) && is_array($rename)) {
  512. foreach ($rename as $rename_name => $renamed_field) {
  513. if ($query) {
  514. $query.= ', ';
  515. }
  516. $field = $changes['rename'][$renamed_field];
  517. $renamed_field = $db->quoteIdentifier($renamed_field, true);
  518. $query.= 'CHANGE ' . $renamed_field . ' ' . $db->getDeclaration($field['definition']['type'], $field['name'], $field['definition']);
  519. }
  520. }
  521. if (!$query) {
  522. return MDB2_OK;
  523. }
  524. $name = $db->quoteIdentifier($name, true);
  525. $result = $db->exec("ALTER TABLE $name $query");
  526. if (MDB2::isError($result)) {
  527. return $result;
  528. }
  529. return MDB2_OK;
  530. }
  531. // }}}
  532. // {{{ listDatabases()
  533. /**
  534. * list all databases
  535. *
  536. * @return mixed array of database names on success, a MDB2 error on failure
  537. * @access public
  538. */
  539. function listDatabases()
  540. {
  541. $db = $this->getDBInstance();
  542. if (MDB2::isError($db)) {
  543. return $db;
  544. }
  545. $result = $db->queryCol('SHOW DATABASES');
  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. // {{{ listUsers()
  556. /**
  557. * list all users
  558. *
  559. * @return mixed array of user names on success, a MDB2 error on failure
  560. * @access public
  561. */
  562. function listUsers()
  563. {
  564. $db = $this->getDBInstance();
  565. if (MDB2::isError($db)) {
  566. return $db;
  567. }
  568. return $db->queryCol('SELECT DISTINCT USER FROM mysql.USER');
  569. }
  570. // }}}
  571. // {{{ listFunctions()
  572. /**
  573. * list all functions in the current database
  574. *
  575. * @return mixed array of function names on success, a MDB2 error on failure
  576. * @access public
  577. */
  578. function listFunctions()
  579. {
  580. $db = $this->getDBInstance();
  581. if (MDB2::isError($db)) {
  582. return $db;
  583. }
  584. $query = "SELECT name FROM mysql.proc";
  585. /*
  586. SELECT ROUTINE_NAME
  587. FROM INFORMATION_SCHEMA.ROUTINES
  588. WHERE ROUTINE_TYPE = 'FUNCTION'
  589. */
  590. $result = $db->queryCol($query);
  591. if (MDB2::isError($result)) {
  592. return $result;
  593. }
  594. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  595. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  596. }
  597. return $result;
  598. }
  599. // }}}
  600. // {{{ listTableTriggers()
  601. /**
  602. * list all triggers in the database that reference a given table
  603. *
  604. * @param string table for which all referenced triggers should be found
  605. * @return mixed array of trigger names on success, a MDB2 error on failure
  606. * @access public
  607. */
  608. function listTableTriggers($table = null)
  609. {
  610. $db = $this->getDBInstance();
  611. if (MDB2::isError($db)) {
  612. return $db;
  613. }
  614. $query = 'SHOW TRIGGERS';
  615. if (null !== $table) {
  616. $table = $db->quote($table, 'text');
  617. $query .= " LIKE $table";
  618. }
  619. $result = $db->queryCol($query);
  620. if (MDB2::isError($result)) {
  621. return $result;
  622. }
  623. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  624. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  625. }
  626. return $result;
  627. }
  628. // }}}
  629. // {{{ listTables()
  630. /**
  631. * list all tables in the current database
  632. *
  633. * @param string database, the current is default
  634. * @return mixed array of table names on success, a MDB2 error on failure
  635. * @access public
  636. */
  637. function listTables($database = null)
  638. {
  639. $db = $this->getDBInstance();
  640. if (MDB2::isError($db)) {
  641. return $db;
  642. }
  643. $query = "SHOW /*!50002 FULL*/ TABLES";
  644. if (null !== $database) {
  645. $query .= " FROM $database";
  646. }
  647. $query.= "/*!50002 WHERE Table_type = 'BASE TABLE'*/";
  648. $table_names = $db->queryAll($query, null, MDB2_FETCHMODE_ORDERED);
  649. if (MDB2::isError($table_names)) {
  650. return $table_names;
  651. }
  652. $result = array();
  653. foreach ($table_names as $table) {
  654. if (!$this->_fixSequenceName($table[0], true)) {
  655. $result[] = $table[0];
  656. }
  657. }
  658. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  659. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  660. }
  661. return $result;
  662. }
  663. // }}}
  664. // {{{ listViews()
  665. /**
  666. * list all views in the current database
  667. *
  668. * @param string database, the current is default
  669. * @return mixed array of view names on success, a MDB2 error on failure
  670. * @access public
  671. */
  672. function listViews($database = null)
  673. {
  674. $db = $this->getDBInstance();
  675. if (MDB2::isError($db)) {
  676. return $db;
  677. }
  678. $query = 'SHOW FULL TABLES';
  679. if (null !== $database) {
  680. $query.= " FROM $database";
  681. }
  682. $query.= " WHERE Table_type = 'VIEW'";
  683. $result = $db->queryCol($query);
  684. if (MDB2::isError($result)) {
  685. return $result;
  686. }
  687. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  688. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  689. }
  690. return $result;
  691. }
  692. // }}}
  693. // {{{ listTableFields()
  694. /**
  695. * list all fields in a table in the current database
  696. *
  697. * @param string $table name of table that should be used in method
  698. * @return mixed array of field names on success, a MDB2 error on failure
  699. * @access public
  700. */
  701. function listTableFields($table)
  702. {
  703. $db = $this->getDBInstance();
  704. if (MDB2::isError($db)) {
  705. return $db;
  706. }
  707. $table = $db->quoteIdentifier($table, true);
  708. $result = $db->queryCol("SHOW COLUMNS FROM $table");
  709. if (MDB2::isError($result)) {
  710. return $result;
  711. }
  712. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  713. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  714. }
  715. return $result;
  716. }
  717. // }}}
  718. // {{{ createIndex()
  719. /**
  720. * Get the stucture of a field into an array
  721. *
  722. * @author Leoncx
  723. * @param string $table name of the table on which the index is to be created
  724. * @param string $name name of the index to be created
  725. * @param array $definition associative array that defines properties of the index to be created.
  726. * Currently, only one property named FIELDS is supported. This property
  727. * is also an associative with the names of the index fields as array
  728. * indexes. Each entry of this array is set to another type of associative
  729. * array that specifies properties of the index that are specific to
  730. * each field.
  731. *
  732. * Currently, only the sorting property is supported. It should be used
  733. * to define the sorting direction of the index. It may be set to either
  734. * ascending or descending.
  735. *
  736. * Not all DBMS support index sorting direction configuration. The DBMS
  737. * drivers of those that do not support it ignore this property. Use the
  738. * function supports() to determine whether the DBMS driver can manage indexes.
  739. *
  740. * Example
  741. * array(
  742. * 'fields' => array(
  743. * 'user_name' => array(
  744. * 'sorting' => 'ascending'
  745. * 'length' => 10
  746. * ),
  747. * 'last_login' => array()
  748. * )
  749. * )
  750. *
  751. * @return mixed MDB2_OK on success, a MDB2 error on failure
  752. * @access public
  753. */
  754. function createIndex($table, $name, $definition)
  755. {
  756. $db = $this->getDBInstance();
  757. if (MDB2::isError($db)) {
  758. return $db;
  759. }
  760. $table = $db->quoteIdentifier($table, true);
  761. $name = $db->quoteIdentifier($db->getIndexName($name), true);
  762. $query = "CREATE INDEX $name ON $table";
  763. $fields = array();
  764. foreach ($definition['fields'] as $field => $fieldinfo) {
  765. if (!empty($fieldinfo['length'])) {
  766. $fields[] = $db->quoteIdentifier($field, true) . '(' . $fieldinfo['length'] . ')';
  767. } else {
  768. $fields[] = $db->quoteIdentifier($field, true);
  769. }
  770. }
  771. $query .= ' ('. implode(', ', $fields) . ')';
  772. $result = $db->exec($query);
  773. if (MDB2::isError($result)) {
  774. return $result;
  775. }
  776. return MDB2_OK;
  777. }
  778. // }}}
  779. // {{{ dropIndex()
  780. /**
  781. * drop existing index
  782. *
  783. * @param string $table name of table that should be used in method
  784. * @param string $name name of the index to be dropped
  785. * @return mixed MDB2_OK on success, a MDB2 error on failure
  786. * @access public
  787. */
  788. function dropIndex($table, $name)
  789. {
  790. $db = $this->getDBInstance();
  791. if (MDB2::isError($db)) {
  792. return $db;
  793. }
  794. $table = $db->quoteIdentifier($table, true);
  795. $name = $db->quoteIdentifier($db->getIndexName($name), true);
  796. $result = $db->exec("DROP INDEX $name ON $table");
  797. if (MDB2::isError($result)) {
  798. return $result;
  799. }
  800. return MDB2_OK;
  801. }
  802. // }}}
  803. // {{{ listTableIndexes()
  804. /**
  805. * list all indexes in a table
  806. *
  807. * @param string $table name of table that should be used in method
  808. * @return mixed array of index names on success, a MDB2 error on failure
  809. * @access public
  810. */
  811. function listTableIndexes($table)
  812. {
  813. $db = $this->getDBInstance();
  814. if (MDB2::isError($db)) {
  815. return $db;
  816. }
  817. $key_name = 'Key_name';
  818. $non_unique = 'Non_unique';
  819. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  820. if ($db->options['field_case'] == CASE_LOWER) {
  821. $key_name = strtolower($key_name);
  822. $non_unique = strtolower($non_unique);
  823. } else {
  824. $key_name = strtoupper($key_name);
  825. $non_unique = strtoupper($non_unique);
  826. }
  827. }
  828. $table = $db->quoteIdentifier($table, true);
  829. $query = "SHOW INDEX FROM $table";
  830. $indexes = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
  831. if (MDB2::isError($indexes)) {
  832. return $indexes;
  833. }
  834. $result = array();
  835. foreach ($indexes as $index_data) {
  836. if ($index_data[$non_unique] && ($index = $this->_fixIndexName($index_data[$key_name]))) {
  837. $result[$index] = true;
  838. }
  839. }
  840. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  841. $result = array_change_key_case($result, $db->options['field_case']);
  842. }
  843. return array_keys($result);
  844. }
  845. // }}}
  846. // {{{ createConstraint()
  847. /**
  848. * create a constraint on a table
  849. *
  850. * @param string $table name of the table on which the constraint is to be created
  851. * @param string $name name of the constraint to be created
  852. * @param array $definition associative array that defines properties of the constraint to be created.
  853. * Currently, only one property named FIELDS is supported. This property
  854. * is also an associative with the names of the constraint fields as array
  855. * constraints. Each entry of this array is set to another type of associative
  856. * array that specifies properties of the constraint that are specific to
  857. * each field.
  858. *
  859. * Example
  860. * array(
  861. * 'fields' => array(
  862. * 'user_name' => array(),
  863. * 'last_login' => array()
  864. * )
  865. * )
  866. * @return mixed MDB2_OK on success, a MDB2 error on failure
  867. * @access public
  868. */
  869. function createConstraint($table, $name, $definition)
  870. {
  871. $db = $this->getDBInstance();
  872. if (MDB2::isError($db)) {
  873. return $db;
  874. }
  875. $type = '';
  876. $idx_name = $db->quoteIdentifier($db->getIndexName($name), true);
  877. if (!empty($definition['primary'])) {
  878. $type = 'PRIMARY';
  879. $idx_name = 'KEY';
  880. } elseif (!empty($definition['unique'])) {
  881. $type = 'UNIQUE';
  882. } elseif (!empty($definition['foreign'])) {
  883. $type = 'CONSTRAINT';
  884. }
  885. if (empty($type)) {
  886. return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  887. 'invalid definition, could not create constraint', __FUNCTION__);
  888. }
  889. $table_quoted = $db->quoteIdentifier($table, true);
  890. $query = "ALTER TABLE $table_quoted ADD $type $idx_name";
  891. if (!empty($definition['foreign'])) {
  892. $query .= ' FOREIGN KEY';
  893. }
  894. $fields = array();
  895. foreach ($definition['fields'] as $field => $fieldinfo) {
  896. $quoted = $db->quoteIdentifier($field, true);
  897. if (!empty($fieldinfo['length'])) {
  898. $quoted .= '(' . $fieldinfo['length'] . ')';
  899. }
  900. $fields[] = $quoted;
  901. }
  902. $query .= ' ('. implode(', ', $fields) . ')';
  903. if (!empty($definition['foreign'])) {
  904. $query.= ' REFERENCES ' . $db->quoteIdentifier($definition['references']['table'], true);
  905. $referenced_fields = array();
  906. foreach (array_keys($definition['references']['fields']) as $field) {
  907. $referenced_fields[] = $db->quoteIdentifier($field, true);
  908. }
  909. $query .= ' ('. implode(', ', $referenced_fields) . ')';
  910. $query .= $this->_getAdvancedFKOptions($definition);
  911. // add index on FK column(s) or we can't add a FK constraint
  912. // @see http://forums.mysql.com/read.php?22,19755,226009
  913. $result = $this->createIndex($table, $name.'_fkidx', $definition);
  914. if (MDB2::isError($result)) {
  915. return $result;
  916. }
  917. }
  918. $res = $db->exec($query);
  919. if (MDB2::isError($res)) {
  920. return $res;
  921. }
  922. if (!empty($definition['foreign'])) {
  923. return $this->_createFKTriggers($table, array($name => $definition));
  924. }
  925. return MDB2_OK;
  926. }
  927. // }}}
  928. // {{{ dropConstraint()
  929. /**
  930. * drop existing constraint
  931. *
  932. * @param string $table name of table that should be used in method
  933. * @param string $name name of the constraint to be dropped
  934. * @param string $primary hint if the constraint is primary
  935. * @return mixed MDB2_OK on success, a MDB2 error on failure
  936. * @access public
  937. */
  938. function dropConstraint($table, $name, $primary = false)
  939. {
  940. $db = $this->getDBInstance();
  941. if (MDB2::isError($db)) {
  942. return $db;
  943. }
  944. if ($primary || strtolower($name) == 'primary') {
  945. $query = 'ALTER TABLE '. $db->quoteIdentifier($table, true) .' DROP PRIMARY KEY';
  946. $result = $db->exec($query);
  947. if (MDB2::isError($result)) {
  948. return $result;
  949. }
  950. return MDB2_OK;
  951. }
  952. //is it a FK constraint? If so, also delete the associated triggers
  953. $db->loadModule('Reverse', null, true);
  954. $definition = $db->reverse->getTableConstraintDefinition($table, $name);
  955. if (!MDB2::isError($definition) && !empty($definition['foreign'])) {
  956. //first drop the FK enforcing triggers
  957. $result = $this->_dropFKTriggers($table, $name, $definition['references']['table']);
  958. if (MDB2::isError($result)) {
  959. return $result;
  960. }
  961. //then drop the constraint itself
  962. $table = $db->quoteIdentifier($table, true);
  963. $name = $db->quoteIdentifier($db->getIndexName($name), true);
  964. $query = "ALTER TABLE $table DROP FOREIGN KEY $name";
  965. $result = $db->exec($query);
  966. if (MDB2::isError($result)) {
  967. return $result;
  968. }
  969. return MDB2_OK;
  970. }
  971. $table = $db->quoteIdentifier($table, true);
  972. $name = $db->quoteIdentifier($db->getIndexName($name), true);
  973. $query = "ALTER TABLE $table DROP INDEX $name";
  974. $result = $db->exec($query);
  975. if (MDB2::isError($result)) {
  976. return $result;
  977. }
  978. return MDB2_OK;
  979. }
  980. // }}}
  981. // {{{ _createFKTriggers()
  982. /**
  983. * Create triggers to enforce the FOREIGN KEY constraint on the table
  984. *
  985. * NB: since there's no RAISE_APPLICATION_ERROR facility in mysql,
  986. * we call a non-existent procedure to raise the FK violation message.
  987. * @see http://forums.mysql.com/read.php?99,55108,71877#msg-71877
  988. *
  989. * @param string $table table name
  990. * @param array $foreign_keys FOREIGN KEY definitions
  991. *
  992. * @return mixed MDB2_OK on success, a MDB2 error on failure
  993. * @access private
  994. */
  995. function _createFKTriggers($table, $foreign_keys)
  996. {
  997. $db = $this->getDBInstance();
  998. if (MDB2::isError($db)) {
  999. return $db;
  1000. }
  1001. // create triggers to enforce FOREIGN KEY constraints
  1002. if ($db->supports('triggers') && !empty($foreign_keys)) {
  1003. $table_quoted = $db->quoteIdentifier($table, true);
  1004. foreach ($foreign_keys as $fkname => $fkdef) {
  1005. if (empty($fkdef)) {
  1006. continue;
  1007. }
  1008. //set actions to default if not set
  1009. $fkdef['onupdate'] = empty($fkdef['onupdate']) ? $db->options['default_fk_action_onupdate'] : strtoupper($fkdef['onupdate']);
  1010. $fkdef['ondelete'] = empty($fkdef['ondelete']) ? $db->options['default_fk_action_ondelete'] : strtoupper($fkdef['ondelete']);
  1011. $trigger_names = array(
  1012. 'insert' => $fkname.'_insert_trg',
  1013. 'update' => $fkname.'_update_trg',
  1014. 'pk_update' => $fkname.'_pk_update_trg',
  1015. 'pk_delete' => $fkname.'_pk_delete_trg',
  1016. );
  1017. $table_fields = array_keys($fkdef['fields']);
  1018. $referenced_fields = array_keys($fkdef['references']['fields']);
  1019. //create the ON [UPDATE|DELETE] triggers on the primary table
  1020. $restrict_action = ' IF (SELECT ';
  1021. $aliased_fields = array();
  1022. foreach ($table_fields as $field) {
  1023. $aliased_fields[] = $table_quoted .'.'.$field .' AS '.$field;
  1024. }
  1025. $restrict_action .= implode(',', $aliased_fields)
  1026. .' FROM '.$table_quoted
  1027. .' WHERE ';
  1028. $conditions = array();
  1029. $new_values = array();
  1030. $null_values = array();
  1031. for ($i=0; $i<count($table_fields); $i++) {
  1032. $conditions[] = $table_fields[$i] .' = OLD.'.$referenced_fields[$i];
  1033. $new_values[] = $table_fields[$i] .' = NEW.'.$referenced_fields[$i];
  1034. $null_values[] = $table_fields[$i] .' = NULL';
  1035. }
  1036. $conditions2 = array();
  1037. for ($i=0; $i<count($referenced_fields); $i++) {
  1038. $conditions2[] = 'NEW.'.$referenced_fields[$i] .' <> OLD.'.$referenced_fields[$i];
  1039. }
  1040. $restrict_action .= implode(' AND ', $conditions).') IS NOT NULL';
  1041. $restrict_action2 = empty($conditions2) ? '' : ' AND (' .implode(' OR ', $conditions2) .')';
  1042. $restrict_action3 = ' THEN CALL %s_ON_TABLE_'.$table.'_VIOLATES_FOREIGN_KEY_CONSTRAINT();'
  1043. .' END IF;';
  1044. $restrict_action_update = $restrict_action . $restrict_action2 . $restrict_action3;
  1045. $restrict_action_delete = $restrict_action . $restrict_action3; // There is no NEW row in on DELETE trigger
  1046. $cascade_action_update = 'UPDATE '.$table_quoted.' SET '.implode(', ', $new_values) .' WHERE '.implode(' AND ', $conditions). ';';
  1047. $cascade_action_delete = 'DELETE FROM '.$table_quoted.' WHERE '.implode(' AND ', $conditions). ';';
  1048. $setnull_action = 'UPDATE '.$table_quoted.' SET '.implode(', ', $null_values).' WHERE '.implode(' AND ', $conditions). ';';
  1049. if ('SET DEFAULT' == $fkdef['onupdate'] || 'SET DEFAULT' == $fkdef['ondelete']) {
  1050. $db->loadModule('Reverse', null, true);
  1051. $default_values = array();
  1052. foreach ($table_fields as $table_field) {
  1053. $field_definition = $db->reverse->getTableFieldDefinition($table, $field);
  1054. if (MDB2::isError($field_definition)) {
  1055. return $field_definition;
  1056. }
  1057. $default_values[] = $table_field .' = '. $field_definition[0]['default'];
  1058. }
  1059. $setdefault_action = 'UPDATE '.$table_quoted.' SET '.implode(', ', $default_values).' WHERE '.implode(' AND ', $conditions). ';';
  1060. }
  1061. $query = 'CREATE TRIGGER %s'
  1062. .' %s ON '.$fkdef['references']['table']
  1063. .' FOR EACH ROW BEGIN '
  1064. .' SET FOREIGN_KEY_CHECKS = 0; '; //only really needed for ON UPDATE CASCADE
  1065. if ('CASCADE' == $fkdef['onupdate']) {
  1066. $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $cascade_action_update;
  1067. } elseif ('SET NULL' == $fkdef['onupdate']) {
  1068. $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action;
  1069. } elseif ('SET DEFAULT' == $fkdef['onupdate']) {
  1070. $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action;
  1071. } elseif ('NO ACTION' == $fkdef['onupdate']) {
  1072. $sql_update = sprintf($query.$restrict_action_update, $trigger_names['pk_update'], 'AFTER UPDATE', 'update');
  1073. } elseif ('RESTRICT' == $fkdef['onupdate']) {
  1074. $sql_update = sprintf($query.$restrict_action_update, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update');
  1075. }
  1076. if ('CASCADE' == $fkdef['ondelete']) {
  1077. $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $cascade_action_delete;
  1078. } elseif ('SET NULL' == $fkdef['ondelete']) {
  1079. $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action;
  1080. } elseif ('SET DEFAULT' == $fkdef['ondelete']) {
  1081. $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action;
  1082. } elseif ('NO ACTION' == $fkdef['ondelete']) {
  1083. $sql_delete = sprintf($query.$restrict_action_delete, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete');
  1084. } elseif ('RESTRICT' == $fkdef['ondelete']) {
  1085. $sql_delete = sprintf($query.$restrict_action_delete, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete');
  1086. }
  1087. $sql_update .= ' SET FOREIGN_KEY_CHECKS = 1; END;';
  1088. $sql_delete .= ' SET FOREIGN_KEY_CHECKS = 1; END;';
  1089. $db->pushErrorHandling(PEAR_ERROR_RETURN);
  1090. $db->expectError(MDB2_ERROR_CANNOT_CREATE);
  1091. $result = $db->exec($sql_delete);
  1092. $expected_errmsg = 'This MySQL version doesn\'t support multiple triggers with the same action time and event for one table';
  1093. $db->popExpect();
  1094. $db->popErrorHandling();
  1095. if (MDB2::isError($result)) {
  1096. if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
  1097. return $result;
  1098. }
  1099. $db->warnings[] = $expected_errmsg;
  1100. }
  1101. $db->pushErrorHandling(PEAR_ERROR_RETURN);
  1102. $db->expectError(MDB2_ERROR_CANNOT_CREATE);
  1103. $result = $db->exec($sql_update);
  1104. $db->popExpect();
  1105. $db->popErrorHandling();
  1106. if (MDB2::isError($result) && $result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
  1107. if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
  1108. return $result;
  1109. }
  1110. $db->warnings[] = $expected_errmsg;
  1111. }
  1112. }
  1113. }
  1114. return MDB2_OK;
  1115. }
  1116. // }}}
  1117. // {{{ _dropFKTriggers()
  1118. /**
  1119. * Drop the triggers created to enforce the FOREIGN KEY constraint on the table
  1120. *
  1121. * @param string $table table name
  1122. * @param string $fkname FOREIGN KEY constraint name
  1123. * @param string $referenced_table referenced table name
  1124. *
  1125. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1126. * @access private
  1127. */
  1128. function _dropFKTriggers($table, $fkname, $referenced_table)
  1129. {
  1130. $db = $this->getDBInstance();
  1131. if (MDB2::isError($db)) {
  1132. return $db;
  1133. }
  1134. $triggers = $this->listTableTriggers($table);
  1135. $triggers2 = $this->listTableTriggers($referenced_table);
  1136. if (!MDB2::isError($triggers2) && !MDB2::isError($triggers)) {
  1137. $triggers = array_merge($triggers, $triggers2);
  1138. $pattern = '/^'.$fkname.'(_pk)?_(insert|update|delete)_trg$/i';
  1139. foreach ($triggers as $trigger) {
  1140. if (preg_match($pattern, $trigger)) {
  1141. $result = $db->exec('DROP TRIGGER '.$trigger);
  1142. if (MDB2::isError($result)) {
  1143. return $result;
  1144. }
  1145. }
  1146. }
  1147. }
  1148. return MDB2_OK;
  1149. }
  1150. // }}}
  1151. // {{{ listTableConstraints()
  1152. /**
  1153. * list all constraints in a table
  1154. *
  1155. * @param string $table name of table that should be used in method
  1156. * @return mixed array of constraint names on success, a MDB2 error on failure
  1157. * @access public
  1158. */
  1159. function listTableConstraints($table)
  1160. {
  1161. $db = $this->getDBInstance();
  1162. if (MDB2::isError($db)) {
  1163. return $db;
  1164. }
  1165. $key_name = 'Key_name';
  1166. $non_unique = 'Non_unique';
  1167. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  1168. if ($db->options['field_case'] == CASE_LOWER) {
  1169. $key_name = strtolower($key_name);
  1170. $non_unique = strtolower($non_unique);
  1171. } else {
  1172. $key_name = strtoupper($key_name);
  1173. $non_unique = strtoupper($non_unique);
  1174. }
  1175. }
  1176. $query = 'SHOW INDEX FROM ' . $db->quoteIdentifier($table, true);
  1177. $indexes = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
  1178. if (MDB2::isError($indexes)) {
  1179. return $indexes;
  1180. }
  1181. $result = array();
  1182. foreach ($indexes as $index_data) {
  1183. if (!$index_data[$non_unique]) {
  1184. if ($index_data[$key_name] !== 'PRIMARY') {
  1185. $index = $this->_fixIndexName($index_data[$key_name]);
  1186. } else {
  1187. $index = 'PRIMARY';
  1188. }
  1189. if (!empty($index)) {
  1190. $result[$index] = true;
  1191. }
  1192. }
  1193. }
  1194. //list FOREIGN KEY constraints...
  1195. $query = 'SHOW CREATE TABLE '. $db->escape($table);
  1196. $definition = $db->queryOne($query, 'text', 1);
  1197. if (!MDB2::isError($definition) && !empty($definition)) {
  1198. $pattern = '/\bCONSTRAINT\b\s+([^\s]+)\s+\bFOREIGN KEY\b/Uims';
  1199. if (preg_match_all($pattern, str_replace('`', '', $definition), $matches) > 0) {
  1200. foreach ($matches[1] as $constraint) {
  1201. $result[$constraint] = true;
  1202. }
  1203. }
  1204. }
  1205. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  1206. $result = array_change_key_case($result, $db->options['field_case']);
  1207. }
  1208. return array_keys($result);
  1209. }
  1210. // }}}
  1211. // {{{ createSequence()
  1212. /**
  1213. * create sequence
  1214. *
  1215. * @param string $seq_name name of the sequence to be created
  1216. * @param string $start start value of the sequence; default is 1
  1217. * @param array $options An associative array of table options:
  1218. * array(
  1219. * 'comment' => 'Foo',
  1220. * 'charset' => 'utf8',
  1221. * 'collate' => 'utf8_unicode_ci',
  1222. * 'type' => 'innodb',
  1223. * );
  1224. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1225. * @access public
  1226. */
  1227. function createSequence($seq_name, $start = 1, $options = array())
  1228. {
  1229. $db = $this->getDBInstance();
  1230. if (MDB2::isError($db)) {
  1231. return $db;
  1232. }
  1233. $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
  1234. $seqcol_name = $db->quoteIdentifier($db->options['seqcol_name'], true);
  1235. $options_strings = array();
  1236. if (!empty($options['comment'])) {
  1237. $options_strings['comment'] = 'COMMENT = '.$db->quote($options['comment'], 'text');
  1238. }
  1239. if (!empty($options['charset'])) {
  1240. $options_strings['charset'] = 'DEFAULT CHARACTER SET '.$options['charset'];
  1241. if (!empty($options['collate'])) {
  1242. $options_strings['charset'].= ' COLLATE '.$options['collate'];
  1243. }
  1244. }
  1245. $type = false;
  1246. if (!empty($options['type'])) {
  1247. $type = $options['type'];
  1248. } elseif ($db->options['default_table_type']) {
  1249. $type = $db->options['default_table_type'];
  1250. }
  1251. if ($type) {
  1252. $options_strings[] = "ENGINE = $type";
  1253. }
  1254. $query = "CREATE TABLE $sequence_name ($seqcol_name INT NOT NULL AUTO_INCREMENT, PRIMARY KEY ($seqcol_name))";
  1255. if (!empty($options_strings)) {
  1256. $query .= ' '.implode(' ', $options_strings);
  1257. }
  1258. $res = $db->exec($query);
  1259. if (MDB2::isError($res)) {
  1260. return $res;
  1261. }
  1262. if ($start == 1) {
  1263. return MDB2_OK;
  1264. }
  1265. $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')';
  1266. $res = $db->exec($query);
  1267. if (!MDB2::isError($res)) {
  1268. return MDB2_OK;
  1269. }
  1270. // Handle error
  1271. $result = $db->exec("DROP TABLE $sequence_name");
  1272. if (MDB2::isError($result)) {
  1273. return $db->raiseError($result, null, null,
  1274. 'could not drop inconsistent sequence table', __FUNCTION__);
  1275. }
  1276. return $db->raiseError($res, null, null,
  1277. 'could not create sequence table', __FUNCTION__);
  1278. }
  1279. // }}}
  1280. // {{{ dropSequence()
  1281. /**
  1282. * drop existing sequence
  1283. *
  1284. * @param string $seq_name name of the sequence to be dropped
  1285. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1286. * @access public
  1287. */
  1288. function dropSequence($seq_name)
  1289. {
  1290. $db = $this->getDBInstance();
  1291. if (MDB2::isError($db)) {
  1292. return $db;
  1293. }
  1294. $sequence_name = $db->quoteIdentifier($db->getSequenceName($seq_name), true);
  1295. $result = $db->exec("DROP TABLE $sequence_name");
  1296. if (MDB2::isError($result)) {
  1297. return $result;
  1298. }
  1299. return MDB2_OK;
  1300. }
  1301. // }}}
  1302. // {{{ listSequences()
  1303. /**
  1304. * list all sequences in the current database
  1305. *
  1306. * @param string database, the current is default
  1307. * @return mixed array of sequence names on success, a MDB2 error on failure
  1308. * @access public
  1309. */
  1310. function listSequences($database = null)
  1311. {
  1312. $db = $this->getDBInstance();
  1313. if (MDB2::isError($db)) {
  1314. return $db;
  1315. }
  1316. $query = "SHOW TABLES";
  1317. if (null !== $database) {
  1318. $query .= " FROM $database";
  1319. }
  1320. $table_names = $db->queryCol($query);
  1321. if (MDB2::isError($table_names)) {
  1322. return $table_names;
  1323. }
  1324. $result = array();
  1325. foreach ($table_names as $table_name) {
  1326. if ($sqn = $this->_fixSequenceName($table_name, true)) {
  1327. $result[] = $sqn;
  1328. }
  1329. }
  1330. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  1331. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  1332. }
  1333. return $result;
  1334. }
  1335. // }}}
  1336. }
  1337. ?>