Compatibility.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <?php
  2. declare(strict_types=1);
  3. namespace PhpMyAdmin\Query;
  4. use PhpMyAdmin\DatabaseInterface;
  5. use PhpMyAdmin\Util;
  6. use function is_string;
  7. use function strlen;
  8. use function strpos;
  9. use function strtoupper;
  10. use function substr;
  11. /**
  12. * Handles data compatibility from SQL query results
  13. */
  14. class Compatibility
  15. {
  16. public static function getISCompatForGetTablesFull(array $eachTables, string $eachDatabase): array
  17. {
  18. foreach ($eachTables as $table_name => $_) {
  19. if (! isset($eachTables[$table_name]['Type'])
  20. && isset($eachTables[$table_name]['Engine'])
  21. ) {
  22. // pma BC, same parts of PMA still uses 'Type'
  23. $eachTables[$table_name]['Type']
  24. =& $eachTables[$table_name]['Engine'];
  25. } elseif (! isset($eachTables[$table_name]['Engine'])
  26. && isset($eachTables[$table_name]['Type'])
  27. ) {
  28. // old MySQL reports Type, newer MySQL reports Engine
  29. $eachTables[$table_name]['Engine']
  30. =& $eachTables[$table_name]['Type'];
  31. }
  32. // Compatibility with INFORMATION_SCHEMA output
  33. $eachTables[$table_name]['TABLE_SCHEMA']
  34. = $eachDatabase;
  35. $eachTables[$table_name]['TABLE_NAME']
  36. =& $eachTables[$table_name]['Name'];
  37. $eachTables[$table_name]['ENGINE']
  38. =& $eachTables[$table_name]['Engine'];
  39. $eachTables[$table_name]['VERSION']
  40. =& $eachTables[$table_name]['Version'];
  41. $eachTables[$table_name]['ROW_FORMAT']
  42. =& $eachTables[$table_name]['Row_format'];
  43. $eachTables[$table_name]['TABLE_ROWS']
  44. =& $eachTables[$table_name]['Rows'];
  45. $eachTables[$table_name]['AVG_ROW_LENGTH']
  46. =& $eachTables[$table_name]['Avg_row_length'];
  47. $eachTables[$table_name]['DATA_LENGTH']
  48. =& $eachTables[$table_name]['Data_length'];
  49. $eachTables[$table_name]['MAX_DATA_LENGTH']
  50. =& $eachTables[$table_name]['Max_data_length'];
  51. $eachTables[$table_name]['INDEX_LENGTH']
  52. =& $eachTables[$table_name]['Index_length'];
  53. $eachTables[$table_name]['DATA_FREE']
  54. =& $eachTables[$table_name]['Data_free'];
  55. $eachTables[$table_name]['AUTO_INCREMENT']
  56. =& $eachTables[$table_name]['Auto_increment'];
  57. $eachTables[$table_name]['CREATE_TIME']
  58. =& $eachTables[$table_name]['Create_time'];
  59. $eachTables[$table_name]['UPDATE_TIME']
  60. =& $eachTables[$table_name]['Update_time'];
  61. $eachTables[$table_name]['CHECK_TIME']
  62. =& $eachTables[$table_name]['Check_time'];
  63. $eachTables[$table_name]['TABLE_COLLATION']
  64. =& $eachTables[$table_name]['Collation'];
  65. $eachTables[$table_name]['CHECKSUM']
  66. =& $eachTables[$table_name]['Checksum'];
  67. $eachTables[$table_name]['CREATE_OPTIONS']
  68. =& $eachTables[$table_name]['Create_options'];
  69. $eachTables[$table_name]['TABLE_COMMENT']
  70. =& $eachTables[$table_name]['Comment'];
  71. if (strtoupper($eachTables[$table_name]['Comment'] ?? '') === 'VIEW'
  72. && $eachTables[$table_name]['Engine'] == null
  73. ) {
  74. $eachTables[$table_name]['TABLE_TYPE'] = 'VIEW';
  75. } elseif ($eachDatabase === 'information_schema') {
  76. $eachTables[$table_name]['TABLE_TYPE'] = 'SYSTEM VIEW';
  77. } else {
  78. /**
  79. * @todo difference between 'TEMPORARY' and 'BASE TABLE'
  80. * but how to detect?
  81. */
  82. $eachTables[$table_name]['TABLE_TYPE'] = 'BASE TABLE';
  83. }
  84. }
  85. return $eachTables;
  86. }
  87. public static function getISCompatForGetColumnsFull(array $columns, string $database, string $table): array
  88. {
  89. $ordinal_position = 1;
  90. foreach ($columns as $column_name => $_) {
  91. // Compatibility with INFORMATION_SCHEMA output
  92. $columns[$column_name]['COLUMN_NAME']
  93. =& $columns[$column_name]['Field'];
  94. $columns[$column_name]['COLUMN_TYPE']
  95. =& $columns[$column_name]['Type'];
  96. $columns[$column_name]['COLLATION_NAME']
  97. =& $columns[$column_name]['Collation'];
  98. $columns[$column_name]['IS_NULLABLE']
  99. =& $columns[$column_name]['Null'];
  100. $columns[$column_name]['COLUMN_KEY']
  101. =& $columns[$column_name]['Key'];
  102. $columns[$column_name]['COLUMN_DEFAULT']
  103. =& $columns[$column_name]['Default'];
  104. $columns[$column_name]['EXTRA']
  105. =& $columns[$column_name]['Extra'];
  106. $columns[$column_name]['PRIVILEGES']
  107. =& $columns[$column_name]['Privileges'];
  108. $columns[$column_name]['COLUMN_COMMENT']
  109. =& $columns[$column_name]['Comment'];
  110. $columns[$column_name]['TABLE_CATALOG'] = null;
  111. $columns[$column_name]['TABLE_SCHEMA'] = $database;
  112. $columns[$column_name]['TABLE_NAME'] = $table;
  113. $columns[$column_name]['ORDINAL_POSITION'] = $ordinal_position;
  114. $colType = $columns[$column_name]['COLUMN_TYPE'];
  115. $colType = is_string($colType) ? $colType : '';
  116. $colTypePosComa = strpos($colType, '(');
  117. $colTypePosComa = $colTypePosComa !== false ? $colTypePosComa : strlen($colType);
  118. $columns[$column_name]['DATA_TYPE']
  119. = substr(
  120. $colType,
  121. 0,
  122. $colTypePosComa
  123. );
  124. /**
  125. * @todo guess CHARACTER_MAXIMUM_LENGTH from COLUMN_TYPE
  126. */
  127. $columns[$column_name]['CHARACTER_MAXIMUM_LENGTH'] = null;
  128. /**
  129. * @todo guess CHARACTER_OCTET_LENGTH from CHARACTER_MAXIMUM_LENGTH
  130. */
  131. $columns[$column_name]['CHARACTER_OCTET_LENGTH'] = null;
  132. $columns[$column_name]['NUMERIC_PRECISION'] = null;
  133. $columns[$column_name]['NUMERIC_SCALE'] = null;
  134. $colCollation = $columns[$column_name]['COLLATION_NAME'];
  135. $colCollation = is_string($colCollation) ? $colCollation : '';
  136. $colCollationPosUnderscore = strpos($colCollation, '_');
  137. $colCollationPosUnderscore = $colCollationPosUnderscore !== false
  138. ? $colCollationPosUnderscore
  139. : strlen($colCollation);
  140. $columns[$column_name]['CHARACTER_SET_NAME']
  141. = substr(
  142. $colCollation,
  143. 0,
  144. $colCollationPosUnderscore
  145. );
  146. $ordinal_position++;
  147. }
  148. return $columns;
  149. }
  150. public static function isMySqlOrPerconaDb(): bool
  151. {
  152. $serverType = Util::getServerType();
  153. return $serverType === 'MySQL' || $serverType === 'Percona Server';
  154. }
  155. public static function isMariaDb(): bool
  156. {
  157. $serverType = Util::getServerType();
  158. return $serverType === 'MariaDB';
  159. }
  160. public static function isCompatibleRenameIndex(int $serverVersion): bool
  161. {
  162. if (self::isMySqlOrPerconaDb()) {
  163. return $serverVersion >= 50700;
  164. }
  165. // @see https://mariadb.com/kb/en/alter-table/#rename-indexkey
  166. if (self::isMariaDb()) {
  167. return $serverVersion >= 100502;
  168. }
  169. return false;
  170. }
  171. public static function supportsReferencesPrivilege(DatabaseInterface $dbi): bool
  172. {
  173. // See: https://mariadb.com/kb/en/grant/#table-privileges
  174. // Unused
  175. if ($dbi->isMariaDB()) {
  176. return false;
  177. }
  178. // https://dev.mysql.com/doc/refman/5.6/en/privileges-provided.html#priv_references
  179. // This privilege is unused before MySQL 5.6.22.
  180. // As of 5.6.22, creation of a foreign key constraint
  181. // requires at least one of the SELECT, INSERT, UPDATE, DELETE,
  182. // or REFERENCES privileges for the parent table.
  183. return $dbi->getVersion() >= 50622;
  184. }
  185. }