Generator.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. <?php
  2. declare(strict_types=1);
  3. namespace PhpMyAdmin\Query;
  4. use PhpMyAdmin\Util;
  5. use function count;
  6. use function implode;
  7. use function is_array;
  8. use function sprintf;
  9. /**
  10. * Handles generating SQL queries
  11. */
  12. class Generator
  13. {
  14. /**
  15. * returns a segment of the SQL WHERE clause regarding table name and type
  16. *
  17. * @param array|string $escapedTableOrTables table(s)
  18. * @param bool $tblIsGroup $table is a table group
  19. * @param string $tableType whether table or view
  20. *
  21. * @return string a segment of the WHERE clause
  22. */
  23. public static function getTableCondition(
  24. $escapedTableOrTables,
  25. bool $tblIsGroup,
  26. ?string $tableType
  27. ): string {
  28. // get table information from information_schema
  29. if ($escapedTableOrTables) {
  30. if (is_array($escapedTableOrTables)) {
  31. $sqlWhereTable = 'AND t.`TABLE_NAME` '
  32. . Util::getCollateForIS() . ' IN (\''
  33. . implode(
  34. '\', \'',
  35. $escapedTableOrTables
  36. )
  37. . '\')';
  38. } elseif ($tblIsGroup === true) {
  39. $sqlWhereTable = 'AND t.`TABLE_NAME` LIKE \''
  40. . Util::escapeMysqlWildcards(
  41. $escapedTableOrTables
  42. )
  43. . '%\'';
  44. } else {
  45. $sqlWhereTable = 'AND t.`TABLE_NAME` '
  46. . Util::getCollateForIS() . ' = \''
  47. . $escapedTableOrTables . '\'';
  48. }
  49. } else {
  50. $sqlWhereTable = '';
  51. }
  52. if ($tableType) {
  53. if ($tableType === 'view') {
  54. $sqlWhereTable .= " AND t.`TABLE_TYPE` NOT IN ('BASE TABLE', 'SYSTEM VERSIONED')";
  55. } elseif ($tableType === 'table') {
  56. $sqlWhereTable .= " AND t.`TABLE_TYPE` IN ('BASE TABLE', 'SYSTEM VERSIONED')";
  57. }
  58. }
  59. return $sqlWhereTable;
  60. }
  61. /**
  62. * returns the beginning of the SQL statement to fetch the list of tables
  63. *
  64. * @param string[] $thisDatabases databases to list
  65. * @param string $sqlWhereTable additional condition
  66. *
  67. * @return string the SQL statement
  68. */
  69. public static function getSqlForTablesFull(array $thisDatabases, string $sqlWhereTable): string
  70. {
  71. return 'SELECT *,'
  72. . ' `TABLE_SCHEMA` AS `Db`,'
  73. . ' `TABLE_NAME` AS `Name`,'
  74. . ' `TABLE_TYPE` AS `TABLE_TYPE`,'
  75. . ' `ENGINE` AS `Engine`,'
  76. . ' `ENGINE` AS `Type`,'
  77. . ' `VERSION` AS `Version`,'
  78. . ' `ROW_FORMAT` AS `Row_format`,'
  79. . ' `TABLE_ROWS` AS `Rows`,'
  80. . ' `AVG_ROW_LENGTH` AS `Avg_row_length`,'
  81. . ' `DATA_LENGTH` AS `Data_length`,'
  82. . ' `MAX_DATA_LENGTH` AS `Max_data_length`,'
  83. . ' `INDEX_LENGTH` AS `Index_length`,'
  84. . ' `DATA_FREE` AS `Data_free`,'
  85. . ' `AUTO_INCREMENT` AS `Auto_increment`,'
  86. . ' `CREATE_TIME` AS `Create_time`,'
  87. . ' `UPDATE_TIME` AS `Update_time`,'
  88. . ' `CHECK_TIME` AS `Check_time`,'
  89. . ' `TABLE_COLLATION` AS `Collation`,'
  90. . ' `CHECKSUM` AS `Checksum`,'
  91. . ' `CREATE_OPTIONS` AS `Create_options`,'
  92. . ' `TABLE_COMMENT` AS `Comment`'
  93. . ' FROM `information_schema`.`TABLES` t'
  94. . ' WHERE `TABLE_SCHEMA` ' . Util::getCollateForIS()
  95. . ' IN (\'' . implode("', '", $thisDatabases) . '\')'
  96. . ' ' . $sqlWhereTable;
  97. }
  98. /**
  99. * Returns SQL for fetching information on table indexes (SHOW INDEXES)
  100. *
  101. * @param string $database name of database
  102. * @param string $table name of the table whose indexes are to be retrieved
  103. * @param string $where additional conditions for WHERE
  104. *
  105. * @return string SQL for getting indexes
  106. */
  107. public static function getTableIndexesSql(
  108. string $database,
  109. string $table,
  110. ?string $where = null
  111. ): string {
  112. $sql = 'SHOW INDEXES FROM ' . Util::backquote($database) . '.'
  113. . Util::backquote($table);
  114. if ($where) {
  115. $sql .= ' WHERE (' . $where . ')';
  116. }
  117. return $sql;
  118. }
  119. /**
  120. * Returns SQL query for fetching columns for a table
  121. *
  122. * @param string $database name of database
  123. * @param string $table name of table to retrieve columns from
  124. * @param string|null $escapedColumn name of column, null to show all columns
  125. * @param bool $full whether to return full info or only column names
  126. */
  127. public static function getColumnsSql(
  128. string $database,
  129. string $table,
  130. ?string $escapedColumn = null,
  131. bool $full = false
  132. ): string {
  133. return 'SHOW ' . ($full ? 'FULL' : '') . ' COLUMNS FROM '
  134. . Util::backquote($database) . '.' . Util::backquote($table)
  135. . ($escapedColumn !== null ? " LIKE '"
  136. . $escapedColumn . "'" : '');
  137. }
  138. public static function getInformationSchemaRoutinesRequest(
  139. string $escapedDb,
  140. ?string $routineType,
  141. ?string $escapedRoutineName
  142. ): string {
  143. $query = 'SELECT'
  144. . ' `ROUTINE_SCHEMA` AS `Db`,'
  145. . ' `SPECIFIC_NAME` AS `Name`,'
  146. . ' `ROUTINE_TYPE` AS `Type`,'
  147. . ' `DEFINER` AS `Definer`,'
  148. . ' `LAST_ALTERED` AS `Modified`,'
  149. . ' `CREATED` AS `Created`,'
  150. . ' `SECURITY_TYPE` AS `Security_type`,'
  151. . ' `ROUTINE_COMMENT` AS `Comment`,'
  152. . ' `CHARACTER_SET_CLIENT` AS `character_set_client`,'
  153. . ' `COLLATION_CONNECTION` AS `collation_connection`,'
  154. . ' `DATABASE_COLLATION` AS `Database Collation`,'
  155. . ' `DTD_IDENTIFIER`'
  156. . ' FROM `information_schema`.`ROUTINES`'
  157. . ' WHERE `ROUTINE_SCHEMA` ' . Util::getCollateForIS()
  158. . " = '" . $escapedDb . "'";
  159. if ($routineType !== null) {
  160. $query .= " AND `ROUTINE_TYPE` = '" . $routineType . "'";
  161. }
  162. if ($escapedRoutineName !== null) {
  163. $query .= ' AND `SPECIFIC_NAME`'
  164. . " = '" . $escapedRoutineName . "'";
  165. }
  166. return $query;
  167. }
  168. public static function getInformationSchemaEventsRequest(string $escapedDb, ?string $escapedEventName): string
  169. {
  170. $query = 'SELECT'
  171. . ' `EVENT_SCHEMA` AS `Db`,'
  172. . ' `EVENT_NAME` AS `Name`,'
  173. . ' `DEFINER` AS `Definer`,'
  174. . ' `TIME_ZONE` AS `Time zone`,'
  175. . ' `EVENT_TYPE` AS `Type`,'
  176. . ' `EXECUTE_AT` AS `Execute at`,'
  177. . ' `INTERVAL_VALUE` AS `Interval value`,'
  178. . ' `INTERVAL_FIELD` AS `Interval field`,'
  179. . ' `STARTS` AS `Starts`,'
  180. . ' `ENDS` AS `Ends`,'
  181. . ' `STATUS` AS `Status`,'
  182. . ' `ORIGINATOR` AS `Originator`,'
  183. . ' `CHARACTER_SET_CLIENT` AS `character_set_client`,'
  184. . ' `COLLATION_CONNECTION` AS `collation_connection`, '
  185. . '`DATABASE_COLLATION` AS `Database Collation`'
  186. . ' FROM `information_schema`.`EVENTS`'
  187. . ' WHERE `EVENT_SCHEMA` ' . Util::getCollateForIS()
  188. . " = '" . $escapedDb . "'";
  189. if ($escapedEventName !== null) {
  190. $query .= ' AND `EVENT_NAME`'
  191. . " = '" . $escapedEventName . "'";
  192. }
  193. return $query;
  194. }
  195. public static function getInformationSchemaTriggersRequest(string $escapedDb, ?string $escapedTable): string
  196. {
  197. $query = 'SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_MANIPULATION'
  198. . ', EVENT_OBJECT_TABLE, ACTION_TIMING, ACTION_STATEMENT'
  199. . ', EVENT_OBJECT_SCHEMA, EVENT_OBJECT_TABLE, DEFINER'
  200. . ' FROM information_schema.TRIGGERS'
  201. . ' WHERE EVENT_OBJECT_SCHEMA ' . Util::getCollateForIS() . '='
  202. . ' \'' . $escapedDb . '\'';
  203. if ($escapedTable !== null) {
  204. $query .= ' AND EVENT_OBJECT_TABLE ' . Util::getCollateForIS()
  205. . " = '" . $escapedTable . "';";
  206. }
  207. return $query;
  208. }
  209. public static function getInformationSchemaDataForCreateRequest(string $user, string $host): string
  210. {
  211. return 'SELECT 1 FROM `INFORMATION_SCHEMA`.`USER_PRIVILEGES` '
  212. . "WHERE `PRIVILEGE_TYPE` = 'CREATE USER' AND "
  213. . "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` LIMIT 1";
  214. }
  215. public static function getInformationSchemaDataForGranteeRequest(string $user, string $host): string
  216. {
  217. return 'SELECT 1 FROM ('
  218. . 'SELECT `GRANTEE`, `IS_GRANTABLE` FROM '
  219. . '`INFORMATION_SCHEMA`.`COLUMN_PRIVILEGES` UNION '
  220. . 'SELECT `GRANTEE`, `IS_GRANTABLE` FROM '
  221. . '`INFORMATION_SCHEMA`.`TABLE_PRIVILEGES` UNION '
  222. . 'SELECT `GRANTEE`, `IS_GRANTABLE` FROM '
  223. . '`INFORMATION_SCHEMA`.`SCHEMA_PRIVILEGES` UNION '
  224. . 'SELECT `GRANTEE`, `IS_GRANTABLE` FROM '
  225. . '`INFORMATION_SCHEMA`.`USER_PRIVILEGES`) t '
  226. . "WHERE `IS_GRANTABLE` = 'YES' AND "
  227. . "'''" . $user . "''@''" . $host . "''' LIKE `GRANTEE` LIMIT 1";
  228. }
  229. public static function getInformationSchemaForeignKeyConstraintsRequest(
  230. string $escapedDatabase,
  231. string $tablesListForQueryCsv
  232. ): string {
  233. return 'SELECT'
  234. . ' TABLE_NAME,'
  235. . ' COLUMN_NAME,'
  236. . ' REFERENCED_TABLE_NAME,'
  237. . ' REFERENCED_COLUMN_NAME'
  238. . ' FROM information_schema.key_column_usage'
  239. . ' WHERE referenced_table_name IS NOT NULL'
  240. . " AND TABLE_SCHEMA = '" . $escapedDatabase . "'"
  241. . ' AND TABLE_NAME IN (' . $tablesListForQueryCsv . ')'
  242. . ' AND REFERENCED_TABLE_NAME IN (' . $tablesListForQueryCsv . ');';
  243. }
  244. public static function getInformationSchemaDatabasesFullRequest(
  245. bool $forceStats,
  246. string $sqlWhereSchema,
  247. string $sortBy,
  248. string $sortOrder,
  249. string $limit
  250. ): string {
  251. $sql = 'SELECT *, '
  252. . 'CAST(BIN_NAME AS CHAR CHARACTER SET utf8) AS SCHEMA_NAME'
  253. . ' FROM (';
  254. $sql .= 'SELECT'
  255. . ' BINARY s.SCHEMA_NAME AS BIN_NAME,'
  256. . ' s.DEFAULT_COLLATION_NAME';
  257. if ($forceStats) {
  258. $sql .= ','
  259. . ' COUNT(t.TABLE_SCHEMA) AS SCHEMA_TABLES,'
  260. . ' SUM(t.TABLE_ROWS) AS SCHEMA_TABLE_ROWS,'
  261. . ' SUM(t.DATA_LENGTH) AS SCHEMA_DATA_LENGTH,'
  262. . ' SUM(t.MAX_DATA_LENGTH) AS SCHEMA_MAX_DATA_LENGTH,'
  263. . ' SUM(t.INDEX_LENGTH) AS SCHEMA_INDEX_LENGTH,'
  264. . ' SUM(t.DATA_LENGTH + t.INDEX_LENGTH) AS SCHEMA_LENGTH,'
  265. . ' SUM(IF(t.ENGINE <> \'InnoDB\', t.DATA_FREE, 0)) AS SCHEMA_DATA_FREE';
  266. }
  267. $sql .= ' FROM `information_schema`.SCHEMATA s ';
  268. if ($forceStats) {
  269. $sql .= ' LEFT JOIN `information_schema`.TABLES t'
  270. . ' ON BINARY t.TABLE_SCHEMA = BINARY s.SCHEMA_NAME';
  271. }
  272. $sql .= $sqlWhereSchema
  273. . ' GROUP BY BINARY s.SCHEMA_NAME, s.DEFAULT_COLLATION_NAME'
  274. . ' ORDER BY ';
  275. if ($sortBy === 'SCHEMA_NAME' || $sortBy === 'DEFAULT_COLLATION_NAME') {
  276. $sql .= 'BINARY ';
  277. }
  278. $sql .= Util::backquote($sortBy)
  279. . ' ' . $sortOrder
  280. . $limit;
  281. $sql .= ') a';
  282. return $sql;
  283. }
  284. public static function getInformationSchemaColumnsFullRequest(
  285. ?string $escapedDatabase,
  286. ?string $escapedTable,
  287. ?string $escapedColumn
  288. ): array {
  289. $sqlWheres = [];
  290. $arrayKeys = [];
  291. // get columns information from information_schema
  292. if ($escapedDatabase !== null) {
  293. $sqlWheres[] = '`TABLE_SCHEMA` = \''
  294. . $escapedDatabase . '\' ';
  295. } else {
  296. $arrayKeys[] = 'TABLE_SCHEMA';
  297. }
  298. if ($escapedTable !== null) {
  299. $sqlWheres[] = '`TABLE_NAME` = \''
  300. . $escapedTable . '\' ';
  301. } else {
  302. $arrayKeys[] = 'TABLE_NAME';
  303. }
  304. if ($escapedColumn !== null) {
  305. $sqlWheres[] = '`COLUMN_NAME` = \''
  306. . $escapedColumn . '\' ';
  307. } else {
  308. $arrayKeys[] = 'COLUMN_NAME';
  309. }
  310. // for PMA bc:
  311. // `[SCHEMA_FIELD_NAME]` AS `[SHOW_FULL_COLUMNS_FIELD_NAME]`
  312. $sql = 'SELECT *,'
  313. . ' `COLUMN_NAME` AS `Field`,'
  314. . ' `COLUMN_TYPE` AS `Type`,'
  315. . ' `COLLATION_NAME` AS `Collation`,'
  316. . ' `IS_NULLABLE` AS `Null`,'
  317. . ' `COLUMN_KEY` AS `Key`,'
  318. . ' `COLUMN_DEFAULT` AS `Default`,'
  319. . ' `EXTRA` AS `Extra`,'
  320. . ' `PRIVILEGES` AS `Privileges`,'
  321. . ' `COLUMN_COMMENT` AS `Comment`'
  322. . ' FROM `information_schema`.`COLUMNS`';
  323. if (count($sqlWheres)) {
  324. $sql .= "\n" . ' WHERE ' . implode(' AND ', $sqlWheres);
  325. }
  326. return [$sql, $arrayKeys];
  327. }
  328. /**
  329. * Function to get sql query for renaming the index using SQL RENAME INDEX Syntax
  330. */
  331. public static function getSqlQueryForIndexRename(
  332. string $dbName,
  333. string $tableName,
  334. string $oldIndexName,
  335. string $newIndexName
  336. ): string {
  337. return sprintf(
  338. 'ALTER TABLE %s.%s RENAME INDEX %s TO %s;',
  339. Util::backquote($dbName),
  340. Util::backquote($tableName),
  341. Util::backquote($oldIndexName),
  342. Util::backquote($newIndexName)
  343. );
  344. }
  345. }