PageRenderTime 68ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/pear/MDB2/Driver/Manager/mysqli.php

https://bitbucket.org/Yason/armory
PHP | 1432 lines | 800 code | 132 blank | 500 comment | 188 complexity | 4a3db0eef4e91422e627db75134af300 MD5 | raw file
Possible License(s): GPL-3.0

Large files files are truncated, but you can click here to view the full file

  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: mysqli.php,v 1.100 2008/11/23 20:30:29 quipo Exp $
  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 (PEAR::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 (PEAR::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 (PEAR::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 (PEAR::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 (!is_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 (PEAR::isError($query)) {
  218. return $query;
  219. }
  220. if (!is_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 (PEAR::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 (PEAR::isError($db)) {
  265. return $db;
  266. }
  267. //delete the triggers associated to existing FK constraints
  268. $constraints = $this->listTableConstraints($name);
  269. if (!PEAR::isError($constraints) && !empty($constraints)) {
  270. $db->loadModule('Reverse', null, true);
  271. foreach ($constraints as $constraint) {
  272. $definition = $db->reverse->getTableConstraintDefinition($name, $constraint);
  273. if (!PEAR::isError($definition) && !empty($definition['foreign'])) {
  274. $result = $this->_dropFKTriggers($name, $constraint, $definition['references']['table']);
  275. if (PEAR::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 (PEAR::isError($db)) {
  297. return $db;
  298. }
  299. $name = $db->quoteIdentifier($name, true);
  300. return $db->exec("TRUNCATE TABLE $name");
  301. }
  302. // }}}
  303. // {{{ vacuum()
  304. /**
  305. * Optimize (vacuum) all the tables in the db (or only the specified table)
  306. * and optionally run ANALYZE.
  307. *
  308. * @param string $table table name (all the tables if empty)
  309. * @param array $options an array with driver-specific options:
  310. * - timeout [int] (in seconds) [mssql-only]
  311. * - analyze [boolean] [pgsql and mysql]
  312. * - full [boolean] [pgsql-only]
  313. * - freeze [boolean] [pgsql-only]
  314. *
  315. * @return mixed MDB2_OK success, a MDB2 error on failure
  316. * @access public
  317. */
  318. function vacuum($table = null, $options = array())
  319. {
  320. $db =& $this->getDBInstance();
  321. if (PEAR::isError($db)) {
  322. return $db;
  323. }
  324. if (empty($table)) {
  325. $table = $this->listTables();
  326. if (PEAR::isError($table)) {
  327. return $table;
  328. }
  329. }
  330. if (is_array($table)) {
  331. foreach (array_keys($table) as $k) {
  332. $table[$k] = $db->quoteIdentifier($table[$k], true);
  333. }
  334. $table = implode(', ', $table);
  335. } else {
  336. $table = $db->quoteIdentifier($table, true);
  337. }
  338. $result = $db->exec('OPTIMIZE TABLE '.$table);
  339. if (PEAR::isError($result)) {
  340. return $result;
  341. }
  342. if (!empty($options['analyze'])) {
  343. return $db->exec('ANALYZE TABLE '.$table);
  344. }
  345. return MDB2_OK;
  346. }
  347. // }}}
  348. // {{{ alterTable()
  349. /**
  350. * alter an existing table
  351. *
  352. * @param string $name name of the table that is intended to be changed.
  353. * @param array $changes associative array that contains the details of each type
  354. * of change that is intended to be performed. The types of
  355. * changes that are currently supported are defined as follows:
  356. *
  357. * name
  358. *
  359. * New name for the table.
  360. *
  361. * add
  362. *
  363. * Associative array with the names of fields to be added as
  364. * indexes of the array. The value of each entry of the array
  365. * should be set to another associative array with the properties
  366. * of the fields to be added. The properties of the fields should
  367. * be the same as defined by the MDB2 parser.
  368. *
  369. *
  370. * remove
  371. *
  372. * Associative array with the names of fields to be removed as indexes
  373. * of the array. Currently the values assigned to each entry are ignored.
  374. * An empty array should be used for future compatibility.
  375. *
  376. * rename
  377. *
  378. * Associative array with the names of fields to be renamed as indexes
  379. * of the array. The value of each entry of the array should be set to
  380. * another associative array with the entry named name with the new
  381. * field name and the entry named Declaration that is expected to contain
  382. * the portion of the field declaration already in DBMS specific SQL code
  383. * as it is used in the CREATE TABLE statement.
  384. *
  385. * change
  386. *
  387. * Associative array with the names of the fields to be changed as indexes
  388. * of the array. Keep in mind that if it is intended to change either the
  389. * name of a field and any other properties, the change array entries
  390. * should have the new names of the fields as array indexes.
  391. *
  392. * The value of each entry of the array should be set to another associative
  393. * array with the properties of the fields to that are meant to be changed as
  394. * array entries. These entries should be assigned to the new values of the
  395. * respective properties. The properties of the fields should be the same
  396. * as defined by the MDB2 parser.
  397. *
  398. * Example
  399. * array(
  400. * 'name' => 'userlist',
  401. * 'add' => array(
  402. * 'quota' => array(
  403. * 'type' => 'integer',
  404. * 'unsigned' => 1
  405. * )
  406. * ),
  407. * 'remove' => array(
  408. * 'file_limit' => array(),
  409. * 'time_limit' => array()
  410. * ),
  411. * 'change' => array(
  412. * 'name' => array(
  413. * 'length' => '20',
  414. * 'definition' => array(
  415. * 'type' => 'text',
  416. * 'length' => 20,
  417. * ),
  418. * )
  419. * ),
  420. * 'rename' => array(
  421. * 'sex' => array(
  422. * 'name' => 'gender',
  423. * 'definition' => array(
  424. * 'type' => 'text',
  425. * 'length' => 1,
  426. * 'default' => 'M',
  427. * ),
  428. * )
  429. * )
  430. * )
  431. *
  432. * @param boolean $check indicates whether the function should just check if the DBMS driver
  433. * can perform the requested table alterations if the value is true or
  434. * actually perform them otherwise.
  435. * @access public
  436. *
  437. * @return mixed MDB2_OK on success, a MDB2 error on failure
  438. */
  439. function alterTable($name, $changes, $check)
  440. {
  441. $db =& $this->getDBInstance();
  442. if (PEAR::isError($db)) {
  443. return $db;
  444. }
  445. foreach ($changes as $change_name => $change) {
  446. switch ($change_name) {
  447. case 'add':
  448. case 'remove':
  449. case 'change':
  450. case 'rename':
  451. case 'name':
  452. break;
  453. default:
  454. return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
  455. 'change type "'.$change_name.'" not yet supported', __FUNCTION__);
  456. }
  457. }
  458. if ($check) {
  459. return MDB2_OK;
  460. }
  461. $query = '';
  462. if (!empty($changes['name'])) {
  463. $change_name = $db->quoteIdentifier($changes['name'], true);
  464. $query .= 'RENAME TO ' . $change_name;
  465. }
  466. if (!empty($changes['add']) && is_array($changes['add'])) {
  467. foreach ($changes['add'] as $field_name => $field) {
  468. if ($query) {
  469. $query.= ', ';
  470. }
  471. $query.= 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field);
  472. }
  473. }
  474. if (!empty($changes['remove']) && is_array($changes['remove'])) {
  475. foreach ($changes['remove'] as $field_name => $field) {
  476. if ($query) {
  477. $query.= ', ';
  478. }
  479. $field_name = $db->quoteIdentifier($field_name, true);
  480. $query.= 'DROP ' . $field_name;
  481. }
  482. }
  483. $rename = array();
  484. if (!empty($changes['rename']) && is_array($changes['rename'])) {
  485. foreach ($changes['rename'] as $field_name => $field) {
  486. $rename[$field['name']] = $field_name;
  487. }
  488. }
  489. if (!empty($changes['change']) && is_array($changes['change'])) {
  490. foreach ($changes['change'] as $field_name => $field) {
  491. if ($query) {
  492. $query.= ', ';
  493. }
  494. if (isset($rename[$field_name])) {
  495. $old_field_name = $rename[$field_name];
  496. unset($rename[$field_name]);
  497. } else {
  498. $old_field_name = $field_name;
  499. }
  500. $old_field_name = $db->quoteIdentifier($old_field_name, true);
  501. $query.= "CHANGE $old_field_name " . $db->getDeclaration($field['definition']['type'], $field_name, $field['definition']);
  502. }
  503. }
  504. if (!empty($rename) && is_array($rename)) {
  505. foreach ($rename as $rename_name => $renamed_field) {
  506. if ($query) {
  507. $query.= ', ';
  508. }
  509. $field = $changes['rename'][$renamed_field];
  510. $renamed_field = $db->quoteIdentifier($renamed_field, true);
  511. $query.= 'CHANGE ' . $renamed_field . ' ' . $db->getDeclaration($field['definition']['type'], $field['name'], $field['definition']);
  512. }
  513. }
  514. if (!$query) {
  515. return MDB2_OK;
  516. }
  517. $name = $db->quoteIdentifier($name, true);
  518. return $db->exec("ALTER TABLE $name $query");
  519. }
  520. // }}}
  521. // {{{ listDatabases()
  522. /**
  523. * list all databases
  524. *
  525. * @return mixed array of database names on success, a MDB2 error on failure
  526. * @access public
  527. */
  528. function listDatabases()
  529. {
  530. $db =& $this->getDBInstance();
  531. if (PEAR::isError($db)) {
  532. return $db;
  533. }
  534. $result = $db->queryCol('SHOW DATABASES');
  535. if (PEAR::isError($result)) {
  536. return $result;
  537. }
  538. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  539. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  540. }
  541. return $result;
  542. }
  543. // }}}
  544. // {{{ listUsers()
  545. /**
  546. * list all users
  547. *
  548. * @return mixed array of user names on success, a MDB2 error on failure
  549. * @access public
  550. */
  551. function listUsers()
  552. {
  553. $db =& $this->getDBInstance();
  554. if (PEAR::isError($db)) {
  555. return $db;
  556. }
  557. return $db->queryCol('SELECT DISTINCT USER FROM mysql.USER');
  558. }
  559. // }}}
  560. // {{{ listFunctions()
  561. /**
  562. * list all functions in the current database
  563. *
  564. * @return mixed array of function names on success, a MDB2 error on failure
  565. * @access public
  566. */
  567. function listFunctions()
  568. {
  569. $db =& $this->getDBInstance();
  570. if (PEAR::isError($db)) {
  571. return $db;
  572. }
  573. $query = "SELECT name FROM mysql.proc";
  574. /*
  575. SELECT ROUTINE_NAME
  576. FROM INFORMATION_SCHEMA.ROUTINES
  577. WHERE ROUTINE_TYPE = 'FUNCTION'
  578. */
  579. $result = $db->queryCol($query);
  580. if (PEAR::isError($result)) {
  581. return $result;
  582. }
  583. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  584. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  585. }
  586. return $result;
  587. }
  588. // }}}
  589. // {{{ listTableTriggers()
  590. /**
  591. * list all triggers in the database that reference a given table
  592. *
  593. * @param string table for which all referenced triggers should be found
  594. * @return mixed array of trigger names on success, a MDB2 error on failure
  595. * @access public
  596. */
  597. function listTableTriggers($table = null)
  598. {
  599. $db =& $this->getDBInstance();
  600. if (PEAR::isError($db)) {
  601. return $db;
  602. }
  603. $query = 'SHOW TRIGGERS';
  604. if (!is_null($table)) {
  605. $table = $db->quote($table, 'text');
  606. $query .= " LIKE $table";
  607. }
  608. $result = $db->queryCol($query);
  609. if (PEAR::isError($result)) {
  610. return $result;
  611. }
  612. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  613. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  614. }
  615. return $result;
  616. }
  617. // }}}
  618. // {{{ listTables()
  619. /**
  620. * list all tables in the current database
  621. *
  622. * @param string database, the current is default
  623. * @return mixed array of table names on success, a MDB2 error on failure
  624. * @access public
  625. */
  626. function listTables($database = null)
  627. {
  628. $db =& $this->getDBInstance();
  629. if (PEAR::isError($db)) {
  630. return $db;
  631. }
  632. $query = "SHOW /*!50002 FULL*/ TABLES";
  633. if (!is_null($database)) {
  634. $query .= " FROM $database";
  635. }
  636. $query.= "/*!50002 WHERE Table_type = 'BASE TABLE'*/";
  637. $table_names = $db->queryAll($query, null, MDB2_FETCHMODE_ORDERED);
  638. if (PEAR::isError($table_names)) {
  639. return $table_names;
  640. }
  641. $result = array();
  642. foreach ($table_names as $table) {
  643. if (!$this->_fixSequenceName($table[0], true)) {
  644. $result[] = $table[0];
  645. }
  646. }
  647. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  648. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  649. }
  650. return $result;
  651. }
  652. // }}}
  653. // {{{ listViews()
  654. /**
  655. * list all views in the current database
  656. *
  657. * @param string database, the current is default
  658. * @return mixed array of view names on success, a MDB2 error on failure
  659. * @access public
  660. */
  661. function listViews($database = null)
  662. {
  663. $db =& $this->getDBInstance();
  664. if (PEAR::isError($db)) {
  665. return $db;
  666. }
  667. $query = 'SHOW FULL TABLES';
  668. if (!is_null($database)) {
  669. $query.= " FROM $database";
  670. }
  671. $query.= " WHERE Table_type = 'VIEW'";
  672. $result = $db->queryCol($query);
  673. if (PEAR::isError($result)) {
  674. return $result;
  675. }
  676. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  677. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  678. }
  679. return $result;
  680. }
  681. // }}}
  682. // {{{ listTableFields()
  683. /**
  684. * list all fields in a table in the current database
  685. *
  686. * @param string $table name of table that should be used in method
  687. * @return mixed array of field names on success, a MDB2 error on failure
  688. * @access public
  689. */
  690. function listTableFields($table)
  691. {
  692. $db =& $this->getDBInstance();
  693. if (PEAR::isError($db)) {
  694. return $db;
  695. }
  696. $table = $db->quoteIdentifier($table, true);
  697. $result = $db->queryCol("SHOW COLUMNS FROM $table");
  698. if (PEAR::isError($result)) {
  699. return $result;
  700. }
  701. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  702. $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
  703. }
  704. return $result;
  705. }
  706. // }}}
  707. // {{{ createIndex()
  708. /**
  709. * Get the stucture of a field into an array
  710. *
  711. * @author Leoncx
  712. * @param string $table name of the table on which the index is to be created
  713. * @param string $name name of the index to be created
  714. * @param array $definition associative array that defines properties of the index to be created.
  715. * Currently, only one property named FIELDS is supported. This property
  716. * is also an associative with the names of the index fields as array
  717. * indexes. Each entry of this array is set to another type of associative
  718. * array that specifies properties of the index that are specific to
  719. * each field.
  720. *
  721. * Currently, only the sorting property is supported. It should be used
  722. * to define the sorting direction of the index. It may be set to either
  723. * ascending or descending.
  724. *
  725. * Not all DBMS support index sorting direction configuration. The DBMS
  726. * drivers of those that do not support it ignore this property. Use the
  727. * function supports() to determine whether the DBMS driver can manage indexes.
  728. *
  729. * Example
  730. * array(
  731. * 'fields' => array(
  732. * 'user_name' => array(
  733. * 'sorting' => 'ascending'
  734. * 'length' => 10
  735. * ),
  736. * 'last_login' => array()
  737. * )
  738. * )
  739. *
  740. * @return mixed MDB2_OK on success, a MDB2 error on failure
  741. * @access public
  742. */
  743. function createIndex($table, $name, $definition)
  744. {
  745. $db =& $this->getDBInstance();
  746. if (PEAR::isError($db)) {
  747. return $db;
  748. }
  749. $table = $db->quoteIdentifier($table, true);
  750. $name = $db->quoteIdentifier($db->getIndexName($name), true);
  751. $query = "CREATE INDEX $name ON $table";
  752. $fields = array();
  753. foreach ($definition['fields'] as $field => $fieldinfo) {
  754. if (!empty($fieldinfo['length'])) {
  755. $fields[] = $db->quoteIdentifier($field, true) . '(' . $fieldinfo['length'] . ')';
  756. } else {
  757. $fields[] = $db->quoteIdentifier($field, true);
  758. }
  759. }
  760. $query .= ' ('. implode(', ', $fields) . ')';
  761. return $db->exec($query);
  762. }
  763. // }}}
  764. // {{{ dropIndex()
  765. /**
  766. * drop existing index
  767. *
  768. * @param string $table name of table that should be used in method
  769. * @param string $name name of the index to be dropped
  770. * @return mixed MDB2_OK on success, a MDB2 error on failure
  771. * @access public
  772. */
  773. function dropIndex($table, $name)
  774. {
  775. $db =& $this->getDBInstance();
  776. if (PEAR::isError($db)) {
  777. return $db;
  778. }
  779. $table = $db->quoteIdentifier($table, true);
  780. $name = $db->quoteIdentifier($db->getIndexName($name), true);
  781. return $db->exec("DROP INDEX $name ON $table");
  782. }
  783. // }}}
  784. // {{{ listTableIndexes()
  785. /**
  786. * list all indexes in a table
  787. *
  788. * @param string $table name of table that should be used in method
  789. * @return mixed array of index names on success, a MDB2 error on failure
  790. * @access public
  791. */
  792. function listTableIndexes($table)
  793. {
  794. $db =& $this->getDBInstance();
  795. if (PEAR::isError($db)) {
  796. return $db;
  797. }
  798. $key_name = 'Key_name';
  799. $non_unique = 'Non_unique';
  800. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  801. if ($db->options['field_case'] == CASE_LOWER) {
  802. $key_name = strtolower($key_name);
  803. $non_unique = strtolower($non_unique);
  804. } else {
  805. $key_name = strtoupper($key_name);
  806. $non_unique = strtoupper($non_unique);
  807. }
  808. }
  809. $table = $db->quoteIdentifier($table, true);
  810. $query = "SHOW INDEX FROM $table";
  811. $indexes = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
  812. if (PEAR::isError($indexes)) {
  813. return $indexes;
  814. }
  815. $result = array();
  816. foreach ($indexes as $index_data) {
  817. if ($index_data[$non_unique] && ($index = $this->_fixIndexName($index_data[$key_name]))) {
  818. $result[$index] = true;
  819. }
  820. }
  821. if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  822. $result = array_change_key_case($result, $db->options['field_case']);
  823. }
  824. return array_keys($result);
  825. }
  826. // }}}
  827. // {{{ createConstraint()
  828. /**
  829. * create a constraint on a table
  830. *
  831. * @param string $table name of the table on which the constraint is to be created
  832. * @param string $name name of the constraint to be created
  833. * @param array $definition associative array that defines properties of the constraint to be created.
  834. * Currently, only one property named FIELDS is supported. This property
  835. * is also an associative with the names of the constraint fields as array
  836. * constraints. Each entry of this array is set to another type of associative
  837. * array that specifies properties of the constraint that are specific to
  838. * each field.
  839. *
  840. * Example
  841. * array(
  842. * 'fields' => array(
  843. * 'user_name' => array(),
  844. * 'last_login' => array()
  845. * )
  846. * )
  847. * @return mixed MDB2_OK on success, a MDB2 error on failure
  848. * @access public
  849. */
  850. function createConstraint($table, $name, $definition)
  851. {
  852. $db =& $this->getDBInstance();
  853. if (PEAR::isError($db)) {
  854. return $db;
  855. }
  856. $type = '';
  857. $idx_name = $db->quoteIdentifier($db->getIndexName($name), true);
  858. if (!empty($definition['primary'])) {
  859. $type = 'PRIMARY';
  860. $idx_name = 'KEY';
  861. } elseif (!empty($definition['unique'])) {
  862. $type = 'UNIQUE';
  863. } elseif (!empty($definition['foreign'])) {
  864. $type = 'CONSTRAINT';
  865. }
  866. if (empty($type)) {
  867. return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
  868. 'invalid definition, could not create constraint', __FUNCTION__);
  869. }
  870. $table_quoted = $db->quoteIdentifier($table, true);
  871. $query = "ALTER TABLE $table_quoted ADD $type $idx_name";
  872. if (!empty($definition['foreign'])) {
  873. $query .= ' FOREIGN KEY';
  874. }
  875. $fields = array();
  876. foreach ($definition['fields'] as $field => $fieldinfo) {
  877. $quoted = $db->quoteIdentifier($field, true);
  878. if (!empty($fieldinfo['length'])) {
  879. $quoted .= '(' . $fieldinfo['length'] . ')';
  880. }
  881. $fields[] = $quoted;
  882. }
  883. $query .= ' ('. implode(', ', $fields) . ')';
  884. if (!empty($definition['foreign'])) {
  885. $query.= ' REFERENCES ' . $db->quoteIdentifier($definition['references']['table'], true);
  886. $referenced_fields = array();
  887. foreach (array_keys($definition['references']['fields']) as $field) {
  888. $referenced_fields[] = $db->quoteIdentifier($field, true);
  889. }
  890. $query .= ' ('. implode(', ', $referenced_fields) . ')';
  891. $query .= $this->_getAdvancedFKOptions($definition);
  892. // add index on FK column(s) or we can't add a FK constraint
  893. // @see http://forums.mysql.com/read.php?22,19755,226009
  894. $result = $this->createIndex($table, $name.'_fkidx', $definition);
  895. if (PEAR::isError($result)) {
  896. return $result;
  897. }
  898. }
  899. $res = $db->exec($query);
  900. if (PEAR::isError($res)) {
  901. return $res;
  902. }
  903. if (!empty($definition['foreign'])) {
  904. return $this->_createFKTriggers($table, array($name => $definition));
  905. }
  906. return MDB2_OK;
  907. }
  908. // }}}
  909. // {{{ dropConstraint()
  910. /**
  911. * drop existing constraint
  912. *
  913. * @param string $table name of table that should be used in method
  914. * @param string $name name of the constraint to be dropped
  915. * @param string $primary hint if the constraint is primary
  916. * @return mixed MDB2_OK on success, a MDB2 error on failure
  917. * @access public
  918. */
  919. function dropConstraint($table, $name, $primary = false)
  920. {
  921. $db =& $this->getDBInstance();
  922. if (PEAR::isError($db)) {
  923. return $db;
  924. }
  925. if ($primary || strtolower($name) == 'primary') {
  926. $query = 'ALTER TABLE '. $db->quoteIdentifier($table, true) .' DROP PRIMARY KEY';
  927. return $db->exec($query);
  928. }
  929. //is it a FK constraint? If so, also delete the associated triggers
  930. $db->loadModule('Reverse', null, true);
  931. $definition = $db->reverse->getTableConstraintDefinition($table, $name);
  932. if (!PEAR::isError($definition) && !empty($definition['foreign'])) {
  933. //first drop the FK enforcing triggers
  934. $result = $this->_dropFKTriggers($table, $name, $definition['references']['table']);
  935. if (PEAR::isError($result)) {
  936. return $result;
  937. }
  938. //then drop the constraint itself
  939. $table = $db->quoteIdentifier($table, true);
  940. $name = $db->quoteIdentifier($db->getIndexName($name), true);
  941. $query = "ALTER TABLE $table DROP FOREIGN KEY $name";
  942. return $db->exec($query);
  943. }
  944. $table = $db->quoteIdentifier($table, true);
  945. $name = $db->quoteIdentifier($db->getIndexName($name), true);
  946. $query = "ALTER TABLE $table DROP INDEX $name";
  947. return $db->exec($query);
  948. }
  949. // }}}
  950. // {{{ _createFKTriggers()
  951. /**
  952. * Create triggers to enforce the FOREIGN KEY constraint on the table
  953. *
  954. * NB: since there's no RAISE_APPLICATION_ERROR facility in mysql,
  955. * we call a non-existent procedure to raise the FK violation message.
  956. * @see http://forums.mysql.com/read.php?99,55108,71877#msg-71877
  957. *
  958. * @param string $table table name
  959. * @param array $foreign_keys FOREIGN KEY definitions
  960. *
  961. * @return mixed MDB2_OK on success, a MDB2 error on failure
  962. * @access private
  963. */
  964. function _createFKTriggers($table, $foreign_keys)
  965. {
  966. $db =& $this->getDBInstance();
  967. if (PEAR::isError($db)) {
  968. return $db;
  969. }
  970. // create triggers to enforce FOREIGN KEY constraints
  971. if ($db->supports('triggers') && !empty($foreign_keys)) {
  972. $table_quoted = $db->quoteIdentifier($table, true);
  973. foreach ($foreign_keys as $fkname => $fkdef) {
  974. if (empty($fkdef)) {
  975. continue;
  976. }
  977. //set actions to default if not set
  978. $fkdef['onupdate'] = empty($fkdef['onupdate']) ? $db->options['default_fk_action_onupdate'] : strtoupper($fkdef['onupdate']);
  979. $fkdef['ondelete'] = empty($fkdef['ondelete']) ? $db->options['default_fk_action_ondelete'] : strtoupper($fkdef['ondelete']);
  980. $trigger_names = array(
  981. 'insert' => $fkname.'_insert_trg',
  982. 'update' => $fkname.'_update_trg',
  983. 'pk_update' => $fkname.'_pk_update_trg',
  984. 'pk_delete' => $fkname.'_pk_delete_trg',
  985. );
  986. $table_fields = array_keys($fkdef['fields']);
  987. $referenced_fields = array_keys($fkdef['references']['fields']);
  988. //create the ON [UPDATE|DELETE] triggers on the primary table
  989. $restrict_action = ' IF (SELECT ';
  990. $aliased_fields = array();
  991. foreach ($table_fields as $field) {
  992. $aliased_fields[] = $table_quoted .'.'.$field .' AS '.$field;
  993. }
  994. $restrict_action .= implode(',', $aliased_fields)
  995. .' FROM '.$table_quoted
  996. .' WHERE ';
  997. $conditions = array();
  998. $new_values = array();
  999. $null_values = array();
  1000. for ($i=0; $i<count($table_fields); $i++) {
  1001. $conditions[] = $table_fields[$i] .' = OLD.'.$referenced_fields[$i];
  1002. $new_values[] = $table_fields[$i] .' = NEW.'.$referenced_fields[$i];
  1003. $null_values[] = $table_fields[$i] .' = NULL';
  1004. }
  1005. $conditions2 = array();
  1006. for ($i=0; $i<count($referenced_fields); $i++) {
  1007. $conditions2[] = 'NEW.'.$referenced_fields[$i] .' <> OLD.'.$referenced_fields[$i];
  1008. }
  1009. $restrict_action .= implode(' AND ', $conditions).') IS NOT NULL'
  1010. .' AND (' .implode(' OR ', $conditions2) .')'
  1011. .' THEN CALL %s_ON_TABLE_'.$table.'_VIOLATES_FOREIGN_KEY_CONSTRAINT();'
  1012. .' END IF;';
  1013. $cascade_action_update = 'UPDATE '.$table_quoted.' SET '.implode(', ', $new_values) .' WHERE '.implode(' AND ', $conditions). ';';
  1014. $cascade_action_delete = 'DELETE FROM '.$table_quoted.' WHERE '.implode(' AND ', $conditions). ';';
  1015. $setnull_action = 'UPDATE '.$table_quoted.' SET '.implode(', ', $null_values).' WHERE '.implode(' AND ', $conditions). ';';
  1016. if ('SET DEFAULT' == $fkdef['onupdate'] || 'SET DEFAULT' == $fkdef['ondelete']) {
  1017. $db->loadModule('Reverse', null, true);
  1018. $default_values = array();
  1019. foreach ($table_fields as $table_field) {
  1020. $field_definition = $db->reverse->getTableFieldDefinition($table, $field);
  1021. if (PEAR::isError($field_definition)) {
  1022. return $field_definition;
  1023. }
  1024. $default_values[] = $table_field .' = '. $field_definition[0]['default'];
  1025. }
  1026. $setdefault_action = 'UPDATE '.$table_quoted.' SET '.implode(', ', $default_values).' WHERE '.implode(' AND ', $conditions). ';';
  1027. }
  1028. $query = 'CREATE TRIGGER %s'
  1029. .' %s ON '.$fkdef['references']['table']
  1030. .' FOR EACH ROW BEGIN '
  1031. .' SET FOREIGN_KEY_CHECKS = 0; '; //only really needed for ON UPDATE CASCADE
  1032. if ('CASCADE' == $fkdef['onupdate']) {
  1033. $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $cascade_action_update;
  1034. } elseif ('SET NULL' == $fkdef['onupdate']) {
  1035. $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action;
  1036. } elseif ('SET DEFAULT' == $fkdef['onupdate']) {
  1037. $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action;
  1038. } elseif ('NO ACTION' == $fkdef['onupdate']) {
  1039. $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'AFTER UPDATE', 'update');
  1040. } elseif ('RESTRICT' == $fkdef['onupdate']) {
  1041. $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update');
  1042. }
  1043. if ('CASCADE' == $fkdef['ondelete']) {
  1044. $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $cascade_action_delete;
  1045. } elseif ('SET NULL' == $fkdef['ondelete']) {
  1046. $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action;
  1047. } elseif ('SET DEFAULT' == $fkdef['ondelete']) {
  1048. $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action;
  1049. } elseif ('NO ACTION' == $fkdef['ondelete']) {
  1050. $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete');
  1051. } elseif ('RESTRICT' == $fkdef['ondelete']) {
  1052. $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete');
  1053. }
  1054. $sql_update .= ' SET FOREIGN_KEY_CHECKS = 1; END;';
  1055. $sql_delete .= ' SET FOREIGN_KEY_CHECKS = 1; END;';
  1056. $db->pushErrorHandling(PEAR_ERROR_RETURN);
  1057. $db->expectError(MDB2_ERROR_CANNOT_CREATE);
  1058. $result = $db->exec($sql_delete);
  1059. $expected_errmsg = 'This MySQL version doesn\'t support multiple triggers with the same action time and event for one table';
  1060. $db->popExpect();
  1061. $db->popErrorHandling();
  1062. if (PEAR::isError($result)) {
  1063. if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
  1064. return $result;
  1065. }
  1066. $db->warnings[] = $expected_errmsg;
  1067. }
  1068. $db->pushErrorHandling(PEAR_ERROR_RETURN);
  1069. $db->expectError(MDB2_ERROR_CANNOT_CREATE);
  1070. $result = $db->exec($sql_update);
  1071. $db->popExpect();
  1072. $db->popErrorHandling();
  1073. if (PEAR::isError($result) && $result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
  1074. if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
  1075. return $result;
  1076. }
  1077. $db->warnings[] = $expected_errmsg;
  1078. }
  1079. }
  1080. }
  1081. return MDB2_OK;
  1082. }
  1083. // }}}
  1084. // {{{ _dropFKTriggers()
  1085. /**
  1086. * Drop the triggers created to enforce the FOREIGN KEY constraint on the table
  1087. *
  1088. * @param string $table table name
  1089. * @param string $fkname FOREIGN KEY constraint name
  1090. * @param string $referenced_table referenced table name
  1091. *
  1092. * @return mixed MDB2_OK on success, a MDB2 error on failure
  1093. * @access private
  1094. */
  1095. function _dropFKTriggers($table, $fkname, $referenced_table)
  1096. {
  1097. $db =& $this->getDBInstance();
  1098. if (PEAR::isError($db)) {
  1099. return $db;
  1100. }
  1101. $triggers = $this->listTableTriggers($table);
  1102. $triggers2 = $this->listTableTriggers($referenced_table);
  1103. if (!PEAR::isError($triggers2) && !PEAR::isError($triggers)) {
  1104. $triggers = array_merge($triggers, $triggers2);
  1105. $pattern = '/^'.$fkname.'(_pk)?_(insert|update|delete)_trg$/i';
  1106. foreach ($triggers as $tr

Large files files are truncated, but you can click here to view the full file