Index.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * holds the database index class
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin;
  9. use PhpMyAdmin\IndexColumn;
  10. use PhpMyAdmin\Message;
  11. use PhpMyAdmin\Sanitize;
  12. use PhpMyAdmin\Url;
  13. use PhpMyAdmin\Util;
  14. /**
  15. * Index manipulation class
  16. *
  17. * @package PhpMyAdmin
  18. * @since phpMyAdmin 3.0.0
  19. */
  20. class Index
  21. {
  22. const PRIMARY = 1;
  23. const UNIQUE = 2;
  24. const INDEX = 4;
  25. const SPATIAL = 8;
  26. const FULLTEXT = 16;
  27. /**
  28. * Class-wide storage container for indexes (caching, singleton)
  29. *
  30. * @var array
  31. */
  32. private static $_registry = array();
  33. /**
  34. * @var string The name of the schema
  35. */
  36. private $_schema = '';
  37. /**
  38. * @var string The name of the table
  39. */
  40. private $_table = '';
  41. /**
  42. * @var string The name of the index
  43. */
  44. private $_name = '';
  45. /**
  46. * Columns in index
  47. *
  48. * @var array
  49. */
  50. private $_columns = array();
  51. /**
  52. * The index method used (BTREE, HASH, RTREE).
  53. *
  54. * @var string
  55. */
  56. private $_type = '';
  57. /**
  58. * The index choice (PRIMARY, UNIQUE, INDEX, SPATIAL, FULLTEXT)
  59. *
  60. * @var string
  61. */
  62. private $_choice = '';
  63. /**
  64. * Various remarks.
  65. *
  66. * @var string
  67. */
  68. private $_remarks = '';
  69. /**
  70. * Any comment provided for the index with a COMMENT attribute when the
  71. * index was created.
  72. *
  73. * @var string
  74. */
  75. private $_comment = '';
  76. /**
  77. * @var integer 0 if the index cannot contain duplicates, 1 if it can.
  78. */
  79. private $_non_unique = 0;
  80. /**
  81. * Indicates how the key is packed. NULL if it is not.
  82. *
  83. * @var string
  84. */
  85. private $_packed = null;
  86. /**
  87. * Block size for the index
  88. *
  89. * @var int
  90. */
  91. private $_key_block_size = null;
  92. /**
  93. * Parser option for the index
  94. *
  95. * @var string
  96. */
  97. private $_parser = null;
  98. /**
  99. * Constructor
  100. *
  101. * @param array $params parameters
  102. */
  103. public function __construct(array $params = array())
  104. {
  105. $this->set($params);
  106. }
  107. /**
  108. * Creates(if not already created) and returns the corresponding Index object
  109. *
  110. * @param string $schema database name
  111. * @param string $table table name
  112. * @param string $index_name index name
  113. *
  114. * @return Index corresponding Index object
  115. */
  116. static public function singleton($schema, $table, $index_name = '')
  117. {
  118. Index::_loadIndexes($table, $schema);
  119. if (! isset(Index::$_registry[$schema][$table][$index_name])) {
  120. $index = new Index;
  121. if (strlen($index_name) > 0) {
  122. $index->setName($index_name);
  123. Index::$_registry[$schema][$table][$index->getName()] = $index;
  124. }
  125. return $index;
  126. }
  127. return Index::$_registry[$schema][$table][$index_name];
  128. }
  129. /**
  130. * returns an array with all indexes from the given table
  131. *
  132. * @param string $table table
  133. * @param string $schema schema
  134. *
  135. * @return Index[] array of indexes
  136. */
  137. static public function getFromTable($table, $schema)
  138. {
  139. Index::_loadIndexes($table, $schema);
  140. if (isset(Index::$_registry[$schema][$table])) {
  141. return Index::$_registry[$schema][$table];
  142. }
  143. return array();
  144. }
  145. /**
  146. * Returns an array with all indexes from the given table of the requested types
  147. *
  148. * @param string $table table
  149. * @param string $schema schema
  150. * @param int $choices choices
  151. *
  152. * @return Index[] array of indexes
  153. */
  154. static public function getFromTableByChoice($table, $schema, $choices = 31)
  155. {
  156. $indexes = array();
  157. foreach (self::getFromTable($table, $schema) as $index) {
  158. if (($choices & Index::PRIMARY)
  159. && $index->getChoice() == 'PRIMARY'
  160. ) {
  161. $indexes[] = $index;
  162. }
  163. if (($choices & Index::UNIQUE)
  164. && $index->getChoice() == 'UNIQUE'
  165. ) {
  166. $indexes[] = $index;
  167. }
  168. if (($choices & Index::INDEX)
  169. && $index->getChoice() == 'INDEX'
  170. ) {
  171. $indexes[] = $index;
  172. }
  173. if (($choices & Index::SPATIAL)
  174. && $index->getChoice() == 'SPATIAL'
  175. ) {
  176. $indexes[] = $index;
  177. }
  178. if (($choices & Index::FULLTEXT)
  179. && $index->getChoice() == 'FULLTEXT'
  180. ) {
  181. $indexes[] = $index;
  182. }
  183. }
  184. return $indexes;
  185. }
  186. /**
  187. * return primary if set, false otherwise
  188. *
  189. * @param string $table table
  190. * @param string $schema schema
  191. *
  192. * @return mixed primary index or false if no one exists
  193. */
  194. static public function getPrimary($table, $schema)
  195. {
  196. Index::_loadIndexes($table, $schema);
  197. if (isset(Index::$_registry[$schema][$table]['PRIMARY'])) {
  198. return Index::$_registry[$schema][$table]['PRIMARY'];
  199. }
  200. return false;
  201. }
  202. /**
  203. * Load index data for table
  204. *
  205. * @param string $table table
  206. * @param string $schema schema
  207. *
  208. * @return boolean whether loading was successful
  209. */
  210. static private function _loadIndexes($table, $schema)
  211. {
  212. if (isset(Index::$_registry[$schema][$table])) {
  213. return true;
  214. }
  215. $_raw_indexes = $GLOBALS['dbi']->getTableIndexes($schema, $table);
  216. foreach ($_raw_indexes as $_each_index) {
  217. $_each_index['Schema'] = $schema;
  218. $keyName = $_each_index['Key_name'];
  219. if (! isset(Index::$_registry[$schema][$table][$keyName])) {
  220. $key = new Index($_each_index);
  221. Index::$_registry[$schema][$table][$keyName] = $key;
  222. } else {
  223. $key = Index::$_registry[$schema][$table][$keyName];
  224. }
  225. $key->addColumn($_each_index);
  226. }
  227. return true;
  228. }
  229. /**
  230. * Add column to index
  231. *
  232. * @param array $params column params
  233. *
  234. * @return void
  235. */
  236. public function addColumn(array $params)
  237. {
  238. if (isset($params['Column_name'])
  239. && strlen($params['Column_name']) > 0
  240. ) {
  241. $this->_columns[$params['Column_name']] = new IndexColumn($params);
  242. }
  243. }
  244. /**
  245. * Adds a list of columns to the index
  246. *
  247. * @param array $columns array containing details about the columns
  248. *
  249. * @return void
  250. */
  251. public function addColumns(array $columns)
  252. {
  253. $_columns = array();
  254. if (isset($columns['names'])) {
  255. // coming from form
  256. // $columns[names][]
  257. // $columns[sub_parts][]
  258. foreach ($columns['names'] as $key => $name) {
  259. $sub_part = isset($columns['sub_parts'][$key])
  260. ? $columns['sub_parts'][$key] : '';
  261. $_columns[] = array(
  262. 'Column_name' => $name,
  263. 'Sub_part' => $sub_part,
  264. );
  265. }
  266. } else {
  267. // coming from SHOW INDEXES
  268. // $columns[][name]
  269. // $columns[][sub_part]
  270. // ...
  271. $_columns = $columns;
  272. }
  273. foreach ($_columns as $column) {
  274. $this->addColumn($column);
  275. }
  276. }
  277. /**
  278. * Returns true if $column indexed in this index
  279. *
  280. * @param string $column the column
  281. *
  282. * @return boolean true if $column indexed in this index
  283. */
  284. public function hasColumn($column)
  285. {
  286. return isset($this->_columns[$column]);
  287. }
  288. /**
  289. * Sets index details
  290. *
  291. * @param array $params index details
  292. *
  293. * @return void
  294. */
  295. public function set(array $params)
  296. {
  297. if (isset($params['columns'])) {
  298. $this->addColumns($params['columns']);
  299. }
  300. if (isset($params['Schema'])) {
  301. $this->_schema = $params['Schema'];
  302. }
  303. if (isset($params['Table'])) {
  304. $this->_table = $params['Table'];
  305. }
  306. if (isset($params['Key_name'])) {
  307. $this->_name = $params['Key_name'];
  308. }
  309. if (isset($params['Index_type'])) {
  310. $this->_type = $params['Index_type'];
  311. }
  312. if (isset($params['Comment'])) {
  313. $this->_remarks = $params['Comment'];
  314. }
  315. if (isset($params['Index_comment'])) {
  316. $this->_comment = $params['Index_comment'];
  317. }
  318. if (isset($params['Non_unique'])) {
  319. $this->_non_unique = $params['Non_unique'];
  320. }
  321. if (isset($params['Packed'])) {
  322. $this->_packed = $params['Packed'];
  323. }
  324. if (isset($params['Index_choice'])) {
  325. $this->_choice = $params['Index_choice'];
  326. } else {
  327. if ('PRIMARY' == $this->_name) {
  328. $this->_choice = 'PRIMARY';
  329. } elseif ('FULLTEXT' == $this->_type) {
  330. $this->_choice = 'FULLTEXT';
  331. $this->_type = '';
  332. } elseif ('SPATIAL' == $this->_type) {
  333. $this->_choice = 'SPATIAL';
  334. $this->_type = '';
  335. } elseif ('0' == $this->_non_unique) {
  336. $this->_choice = 'UNIQUE';
  337. } else {
  338. $this->_choice = 'INDEX';
  339. }
  340. }
  341. if (isset($params['Key_block_size'])) {
  342. $this->_key_block_size = $params['Key_block_size'];
  343. }
  344. if (isset($params['Parser'])) {
  345. $this->_parser = $params['Parser'];
  346. }
  347. }
  348. /**
  349. * Returns the number of columns of the index
  350. *
  351. * @return integer the number of the columns
  352. */
  353. public function getColumnCount()
  354. {
  355. return count($this->_columns);
  356. }
  357. /**
  358. * Returns the index comment
  359. *
  360. * @return string index comment
  361. */
  362. public function getComment()
  363. {
  364. return $this->_comment;
  365. }
  366. /**
  367. * Returns index remarks
  368. *
  369. * @return string index remarks
  370. */
  371. public function getRemarks()
  372. {
  373. return $this->_remarks;
  374. }
  375. /**
  376. * Return the key block size
  377. *
  378. * @return number
  379. */
  380. public function getKeyBlockSize()
  381. {
  382. return $this->_key_block_size;
  383. }
  384. /**
  385. * Return the parser
  386. *
  387. * @return string
  388. */
  389. public function getParser()
  390. {
  391. return $this->_parser;
  392. }
  393. /**
  394. * Returns concatenated remarks and comment
  395. *
  396. * @return string concatenated remarks and comment
  397. */
  398. public function getComments()
  399. {
  400. $comments = $this->getRemarks();
  401. if (strlen($comments) > 0) {
  402. $comments .= "\n";
  403. }
  404. $comments .= $this->getComment();
  405. return $comments;
  406. }
  407. /**
  408. * Returns index type (BTREE, HASH, RTREE)
  409. *
  410. * @return string index type
  411. */
  412. public function getType()
  413. {
  414. return $this->_type;
  415. }
  416. /**
  417. * Returns index choice (PRIMARY, UNIQUE, INDEX, SPATIAL, FULLTEXT)
  418. *
  419. * @return string index choice
  420. */
  421. public function getChoice()
  422. {
  423. return $this->_choice;
  424. }
  425. /**
  426. * Return a list of all index choices
  427. *
  428. * @return string[] index choices
  429. */
  430. static public function getIndexChoices()
  431. {
  432. return array(
  433. 'PRIMARY',
  434. 'INDEX',
  435. 'UNIQUE',
  436. 'SPATIAL',
  437. 'FULLTEXT',
  438. );
  439. }
  440. /**
  441. * Returns a lit of all index types
  442. *
  443. * @return string[] index types
  444. */
  445. static public function getIndexTypes()
  446. {
  447. return array(
  448. 'BTREE',
  449. 'HASH'
  450. );
  451. }
  452. /**
  453. * Returns HTML for the index choice selector
  454. *
  455. * @param boolean $edit_table whether this is table editing
  456. *
  457. * @return string HTML for the index choice selector
  458. */
  459. public function generateIndexChoiceSelector($edit_table)
  460. {
  461. $html_options = '<select name="index[Index_choice]"'
  462. . ' id="select_index_choice" '
  463. . ($edit_table ? 'disabled="disabled"' : '') . '>';
  464. foreach (Index::getIndexChoices() as $each_index_choice) {
  465. if ($each_index_choice === 'PRIMARY'
  466. && $this->_choice !== 'PRIMARY'
  467. && Index::getPrimary($this->_table, $this->_schema)
  468. ) {
  469. // skip PRIMARY if there is already one in the table
  470. continue;
  471. }
  472. $html_options .= '<option value="' . $each_index_choice . '"'
  473. . (($this->_choice == $each_index_choice)
  474. ? ' selected="selected"'
  475. : '')
  476. . '>' . $each_index_choice . '</option>' . "\n";
  477. }
  478. $html_options .= '</select>';
  479. return $html_options;
  480. }
  481. /**
  482. * Returns HTML for the index type selector
  483. *
  484. * @return string HTML for the index type selector
  485. */
  486. public function generateIndexTypeSelector()
  487. {
  488. $types = array("" => "--");
  489. foreach (Index::getIndexTypes() as $type) {
  490. $types[$type] = $type;
  491. }
  492. return Util::getDropdown(
  493. "index[Index_type]", $types,
  494. $this->_type, "select_index_type"
  495. );
  496. }
  497. /**
  498. * Returns how the index is packed
  499. *
  500. * @return string how the index is packed
  501. */
  502. public function getPacked()
  503. {
  504. return $this->_packed;
  505. }
  506. /**
  507. * Returns 'No' if the index is not packed,
  508. * how the index is packed if packed
  509. *
  510. * @return string
  511. */
  512. public function isPacked()
  513. {
  514. if (null === $this->_packed) {
  515. return __('No');
  516. }
  517. return htmlspecialchars($this->_packed);
  518. }
  519. /**
  520. * Returns integer 0 if the index cannot contain duplicates, 1 if it can
  521. *
  522. * @return integer 0 if the index cannot contain duplicates, 1 if it can
  523. */
  524. public function getNonUnique()
  525. {
  526. return $this->_non_unique;
  527. }
  528. /**
  529. * Returns whether the index is a 'Unique' index
  530. *
  531. * @param boolean $as_text whether to output should be in text
  532. *
  533. * @return mixed whether the index is a 'Unique' index
  534. */
  535. public function isUnique($as_text = false)
  536. {
  537. if ($as_text) {
  538. $r = array(
  539. '0' => __('Yes'),
  540. '1' => __('No'),
  541. );
  542. } else {
  543. $r = array(
  544. '0' => true,
  545. '1' => false,
  546. );
  547. }
  548. return $r[$this->_non_unique];
  549. }
  550. /**
  551. * Returns the name of the index
  552. *
  553. * @return string the name of the index
  554. */
  555. public function getName()
  556. {
  557. return $this->_name;
  558. }
  559. /**
  560. * Sets the name of the index
  561. *
  562. * @param string $name index name
  563. *
  564. * @return void
  565. */
  566. public function setName($name)
  567. {
  568. $this->_name = (string) $name;
  569. }
  570. /**
  571. * Returns the columns of the index
  572. *
  573. * @return IndexColumn[] the columns of the index
  574. */
  575. public function getColumns()
  576. {
  577. return $this->_columns;
  578. }
  579. /**
  580. * Get HTML for display indexes
  581. *
  582. * @return string $html_output
  583. */
  584. public static function getHtmlForDisplayIndexes()
  585. {
  586. $html_output = '<div id="index_div" class="width100 ajax" >';
  587. $html_output .= self::getHtmlForIndexes(
  588. $GLOBALS['table'],
  589. $GLOBALS['db']
  590. );
  591. $html_output .= '<fieldset class="tblFooters print_ignore" style="text-align: '
  592. . 'left;"><form action="tbl_indexes.php" method="post">';
  593. $html_output .= Url::getHiddenInputs(
  594. $GLOBALS['db'], $GLOBALS['table']
  595. );
  596. $html_output .= sprintf(
  597. __('Create an index on &nbsp;%s&nbsp;columns'),
  598. '<input type="number" name="added_fields" value="1" '
  599. . 'min="1" required="required" />'
  600. );
  601. $html_output .= '<input type="hidden" name="create_index" value="1" />'
  602. . '<input class="add_index ajax"'
  603. . ' type="submit" value="' . __('Go') . '" />';
  604. $html_output .= '</form>'
  605. . '</fieldset>'
  606. . '</div>';
  607. return $html_output;
  608. }
  609. /**
  610. * Show index data
  611. *
  612. * @param string $table The table name
  613. * @param string $schema The schema name
  614. * @param boolean $print_mode Whether the output is for the print mode
  615. *
  616. * @return string HTML for showing index
  617. *
  618. * @access public
  619. */
  620. static public function getHtmlForIndexes($table, $schema, $print_mode = false)
  621. {
  622. $indexes = Index::getFromTable($table, $schema);
  623. $no_indexes_class = count($indexes) > 0 ? ' hide' : '';
  624. $no_indexes = "<div class='no_indexes_defined$no_indexes_class'>";
  625. $no_indexes .= Message::notice(__('No index defined!'))->getDisplay();
  626. $no_indexes .= '</div>';
  627. if (! $print_mode) {
  628. $r = '<fieldset class="index_info">';
  629. $r .= '<legend id="index_header">' . __('Indexes');
  630. $r .= Util::showMySQLDocu('optimizing-database-structure');
  631. $r .= '</legend>';
  632. $r .= $no_indexes;
  633. if (count($indexes) < 1) {
  634. $r .= '</fieldset>';
  635. return $r;
  636. }
  637. $r .= Index::findDuplicates($table, $schema);
  638. } else {
  639. $r = '<h3>' . __('Indexes') . '</h3>';
  640. $r .= $no_indexes;
  641. if (count($indexes) < 1) {
  642. return $r;
  643. }
  644. }
  645. $r .= '<div class="responsivetable jsresponsive">';
  646. $r .= '<table id="table_index">';
  647. $r .= '<thead>';
  648. $r .= '<tr>';
  649. if (! $print_mode) {
  650. $r .= '<th colspan="2" class="print_ignore">' . __('Action') . '</th>';
  651. }
  652. $r .= '<th>' . __('Keyname') . '</th>';
  653. $r .= '<th>' . __('Type') . '</th>';
  654. $r .= '<th>' . __('Unique') . '</th>';
  655. $r .= '<th>' . __('Packed') . '</th>';
  656. $r .= '<th>' . __('Column') . '</th>';
  657. $r .= '<th>' . __('Cardinality') . '</th>';
  658. $r .= '<th>' . __('Collation') . '</th>';
  659. $r .= '<th>' . __('Null') . '</th>';
  660. $r .= '<th>' . __('Comment') . '</th>';
  661. $r .= '</tr>';
  662. $r .= '</thead>';
  663. foreach ($indexes as $index) {
  664. $row_span = ' rowspan="' . $index->getColumnCount() . '" ';
  665. $r .= '<tbody class="row_span">';
  666. $r .= '<tr class="noclick" >';
  667. if (! $print_mode) {
  668. $this_params = $GLOBALS['url_params'];
  669. $this_params['index'] = $index->getName();
  670. $r .= '<td class="edit_index print_ignore';
  671. $r .= ' ajax';
  672. $r .= '" ' . $row_span . '>'
  673. . ' <a class="';
  674. $r .= 'ajax';
  675. $r .= '" href="tbl_indexes.php" data-post="' . Url::getCommon($this_params, '', false)
  676. . '">' . Util::getIcon('b_edit', __('Edit')) . '</a>'
  677. . '</td>' . "\n";
  678. $this_params = $GLOBALS['url_params'];
  679. if ($index->getName() == 'PRIMARY') {
  680. $this_params['sql_query'] = 'ALTER TABLE '
  681. . Util::backquote($table)
  682. . ' DROP PRIMARY KEY;';
  683. $this_params['message_to_show']
  684. = __('The primary key has been dropped.');
  685. $js_msg = Sanitize::jsFormat($this_params['sql_query'], false);
  686. } else {
  687. $this_params['sql_query'] = 'ALTER TABLE '
  688. . Util::backquote($table) . ' DROP INDEX '
  689. . Util::backquote($index->getName()) . ';';
  690. $this_params['message_to_show'] = sprintf(
  691. __('Index %s has been dropped.'), htmlspecialchars($index->getName())
  692. );
  693. $js_msg = Sanitize::jsFormat($this_params['sql_query'], false);
  694. }
  695. $r .= '<td ' . $row_span . ' class="print_ignore">';
  696. $r .= '<input type="hidden" class="drop_primary_key_index_msg"'
  697. . ' value="' . $js_msg . '" />';
  698. $r .= Util::linkOrButton(
  699. 'sql.php',
  700. $this_params,
  701. Util::getIcon('b_drop', __('Drop')),
  702. array('class' => 'drop_primary_key_index_anchor ajax')
  703. );
  704. $r .= '</td>' . "\n";
  705. }
  706. if (! $print_mode) {
  707. $r .= '<th ' . $row_span . '>'
  708. . htmlspecialchars($index->getName())
  709. . '</th>';
  710. } else {
  711. $r .= '<td ' . $row_span . '>'
  712. . htmlspecialchars($index->getName())
  713. . '</td>';
  714. }
  715. $r .= '<td ' . $row_span . '>';
  716. $type = $index->getType();
  717. if (! empty($type)) {
  718. $r .= htmlspecialchars($type);
  719. } else {
  720. $r .= htmlspecialchars($index->getChoice());
  721. }
  722. $r .= '</td>';
  723. $r .= '<td ' . $row_span . '>' . $index->isUnique(true) . '</td>';
  724. $r .= '<td ' . $row_span . '>' . $index->isPacked() . '</td>';
  725. foreach ($index->getColumns() as $column) {
  726. if ($column->getSeqInIndex() > 1) {
  727. $r .= '<tr class="noclick" >';
  728. }
  729. $r .= '<td>' . htmlspecialchars($column->getName());
  730. if ($column->getSubPart()) {
  731. $r .= ' (' . htmlspecialchars($column->getSubPart()) . ')';
  732. }
  733. $r .= '</td>';
  734. $r .= '<td>'
  735. . htmlspecialchars($column->getCardinality())
  736. . '</td>';
  737. $r .= '<td>'
  738. . htmlspecialchars($column->getCollation())
  739. . '</td>';
  740. $r .= '<td>'
  741. . htmlspecialchars($column->getNull(true))
  742. . '</td>';
  743. if ($column->getSeqInIndex() == 1
  744. ) {
  745. $r .= '<td ' . $row_span . '>'
  746. . htmlspecialchars($index->getComments()) . '</td>';
  747. }
  748. $r .= '</tr>';
  749. } // end foreach $index['Sequences']
  750. $r .= '</tbody>';
  751. } // end while
  752. $r .= '</table>';
  753. $r .= '</div>';
  754. if (! $print_mode) {
  755. $r .= '</fieldset>';
  756. }
  757. return $r;
  758. }
  759. /**
  760. * Gets the properties in an array for comparison purposes
  761. *
  762. * @return array an array containing the properties of the index
  763. */
  764. public function getCompareData()
  765. {
  766. $data = array(
  767. // 'Non_unique' => $this->_non_unique,
  768. 'Packed' => $this->_packed,
  769. 'Index_choice' => $this->_choice,
  770. );
  771. foreach ($this->_columns as $column) {
  772. $data['columns'][] = $column->getCompareData();
  773. }
  774. return $data;
  775. }
  776. /**
  777. * Function to check over array of indexes and look for common problems
  778. *
  779. * @param string $table table name
  780. * @param string $schema schema name
  781. *
  782. * @return string Output HTML
  783. * @access public
  784. */
  785. static public function findDuplicates($table, $schema)
  786. {
  787. $indexes = Index::getFromTable($table, $schema);
  788. $output = '';
  789. // count($indexes) < 2:
  790. // there is no need to check if there less than two indexes
  791. if (count($indexes) < 2) {
  792. return $output;
  793. }
  794. // remove last index from stack and ...
  795. while ($while_index = array_pop($indexes)) {
  796. // ... compare with every remaining index in stack
  797. foreach ($indexes as $each_index) {
  798. if ($each_index->getCompareData() !== $while_index->getCompareData()
  799. ) {
  800. continue;
  801. }
  802. // did not find any difference
  803. // so it makes no sense to have this two equal indexes
  804. $message = Message::notice(
  805. __(
  806. 'The indexes %1$s and %2$s seem to be equal and one of them '
  807. . 'could possibly be removed.'
  808. )
  809. );
  810. $message->addParam($each_index->getName());
  811. $message->addParam($while_index->getName());
  812. $output .= $message->getDisplay();
  813. // there is no need to check any further indexes if we have already
  814. // found that this one has a duplicate
  815. continue 2;
  816. }
  817. }
  818. return $output;
  819. }
  820. }