You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
4.5 KiB

3 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Morris Jobke <hey@morrisjobke.de>
  8. * @author Robin Appelman <robin@icewind.nl>
  9. * @author Robin Müller <coder-hugo@users.noreply.github.com>
  10. * @author Thomas Müller <thomas.mueller@tmit.eu>
  11. *
  12. * @license AGPL-3.0
  13. *
  14. * This code is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU Affero General Public License, version 3,
  16. * as published by the Free Software Foundation.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU Affero General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Affero General Public License, version 3,
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>
  25. *
  26. */
  27. namespace OC\Repair;
  28. use Doctrine\DBAL\Exception\DriverException;
  29. use Doctrine\DBAL\Platforms\MySqlPlatform;
  30. use OCP\IConfig;
  31. use OCP\IDBConnection;
  32. use OCP\ILogger;
  33. use OCP\Migration\IOutput;
  34. use OCP\Migration\IRepairStep;
  35. class Collation implements IRepairStep {
  36. /** @var IConfig */
  37. protected $config;
  38. /** @var ILogger */
  39. protected $logger;
  40. /** @var IDBConnection */
  41. protected $connection;
  42. /** @var bool */
  43. protected $ignoreFailures;
  44. /**
  45. * @param IConfig $config
  46. * @param ILogger $logger
  47. * @param IDBConnection $connection
  48. * @param bool $ignoreFailures
  49. */
  50. public function __construct(IConfig $config, ILogger $logger, IDBConnection $connection, $ignoreFailures) {
  51. $this->connection = $connection;
  52. $this->config = $config;
  53. $this->logger = $logger;
  54. $this->ignoreFailures = $ignoreFailures;
  55. }
  56. public function getName() {
  57. return 'Repair MySQL collation';
  58. }
  59. /**
  60. * Fix mime types
  61. */
  62. public function run(IOutput $output) {
  63. if (!$this->connection->getDatabasePlatform() instanceof MySqlPlatform) {
  64. $output->info('Not a mysql database -> nothing to do');
  65. return;
  66. }
  67. $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
  68. $tables = $this->getAllNonUTF8BinTables($this->connection);
  69. foreach ($tables as $table) {
  70. $output->info("Change row format for $table ...");
  71. $query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT = DYNAMIC;');
  72. try {
  73. $query->execute();
  74. } catch (DriverException $e) {
  75. // Just log this
  76. $this->logger->logException($e);
  77. if (!$this->ignoreFailures) {
  78. throw $e;
  79. }
  80. }
  81. $output->info("Change collation for $table ...");
  82. if ($characterSet === 'utf8mb4') {
  83. // need to set row compression first
  84. $query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT=COMPRESSED;');
  85. $query->execute();
  86. }
  87. $query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET ' . $characterSet . ' COLLATE ' . $characterSet . '_bin;');
  88. try {
  89. $query->execute();
  90. } catch (DriverException $e) {
  91. // Just log this
  92. $this->logger->logException($e);
  93. if (!$this->ignoreFailures) {
  94. throw $e;
  95. }
  96. }
  97. }
  98. if (empty($tables)) {
  99. $output->info('All tables already have the correct collation -> nothing to do');
  100. }
  101. }
  102. /**
  103. * @param IDBConnection $connection
  104. * @return string[]
  105. */
  106. protected function getAllNonUTF8BinTables(IDBConnection $connection) {
  107. $dbName = $this->config->getSystemValue("dbname");
  108. $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
  109. // fetch tables by columns
  110. $statement = $connection->executeQuery(
  111. "SELECT DISTINCT(TABLE_NAME) AS `table`" .
  112. " FROM INFORMATION_SCHEMA . COLUMNS" .
  113. " WHERE TABLE_SCHEMA = ?" .
  114. " AND (COLLATION_NAME <> '" . $characterSet . "_bin' OR CHARACTER_SET_NAME <> '" . $characterSet . "')" .
  115. " AND TABLE_NAME LIKE '*PREFIX*%'",
  116. [$dbName]
  117. );
  118. $rows = $statement->fetchAll();
  119. $result = [];
  120. foreach ($rows as $row) {
  121. $result[$row['table']] = true;
  122. }
  123. // fetch tables by collation
  124. $statement = $connection->executeQuery(
  125. "SELECT DISTINCT(TABLE_NAME) AS `table`" .
  126. " FROM INFORMATION_SCHEMA . TABLES" .
  127. " WHERE TABLE_SCHEMA = ?" .
  128. " AND TABLE_COLLATION <> '" . $characterSet . "_bin'" .
  129. " AND TABLE_NAME LIKE '*PREFIX*%'",
  130. [$dbName]
  131. );
  132. $rows = $statement->fetchAll();
  133. foreach ($rows as $row) {
  134. $result[$row['table']] = true;
  135. }
  136. return array_keys($result);
  137. }
  138. }