2 Commits

Author SHA1 Message Date
oysteikt 43e4cc45ca server/sql: great performance improvements for listing databases
Build and test / check-license (push) Successful in 53s
Build and test / check (push) Successful in 1m54s
Build and test / build (push) Successful in 3m5s
Build and test / test (push) Successful in 3m9s
Build and test / docs (push) Successful in 5m36s
2026-05-31 02:01:44 +09:00
oysteikt 62b1b66bb6 CHANGELOG.md: fix broken link
Build and test / check-license (push) Successful in 55s
Build and test / check (push) Successful in 2m38s
Build and test / build (push) Successful in 2m42s
Build and test / test (push) Successful in 3m8s
Build and test / docs (push) Successful in 6m8s
2026-05-31 00:44:52 +09:00
2 changed files with 89 additions and 35 deletions
+1 -1
View File
@@ -76,7 +76,7 @@ This is the initial release of `muscl`.
interactive tool, there shouldn't have been any scripts relying on the old formatting. interactive tool, there shouldn't have been any scripts relying on the old formatting.
- The configuration file is shared for all variants of the program, and `muscl` will use - The configuration file is shared for all variants of the program, and `muscl` will use
its new logic to look for and parse this file. See the example config and its new logic to look for and parse this file. See the example config and
[installation instructions][installation-instructions] for more information about how to [installation instructions](./docs/installation.md) for more information about how to
configure the software. configure the software.
- The order in which input is validated might be differ from the original - The order in which input is validated might be differ from the original
(e.g. database ownership checks, invalid character checks, existence checks, ...). (e.g. database ownership checks, invalid character checks, existence checks, ...).
+88 -34
View File
@@ -272,26 +272,49 @@ pub async fn list_databases(
let result = sqlx::query_as::<_, DatabaseRow>( let result = sqlx::query_as::<_, DatabaseRow>(
r" r"
SELECT SELECT
CAST(`information_schema`.`SCHEMATA`.`SCHEMA_NAME` AS CHAR(64)) AS `database`, CAST(s.SCHEMA_NAME AS CHAR(64)) AS `database`,
GROUP_CONCAT(DISTINCT CAST(`information_schema`.`TABLES`.`TABLE_NAME` AS CHAR(64)) SEPARATOR ',') AS `tables`, t.tables,
GROUP_CONCAT(DISTINCT CAST(`mysql`.`db`.`User` AS CHAR(64)) SEPARATOR ',') AS `users`, u.users,
MAX(`information_schema`.`SCHEMATA`.`DEFAULT_COLLATION_NAME`) AS `collation`, s.DEFAULT_COLLATION_NAME AS `collation`,
MAX(`information_schema`.`SCHEMATA`.`DEFAULT_CHARACTER_SET_NAME`) AS `character_set`, s.DEFAULT_CHARACTER_SET_NAME AS `character_set`,
CAST(IFNULL( CAST(COALESCE(t.size_bytes, 0) AS UNSIGNED) AS `size_bytes`
SUM(`information_schema`.`TABLES`.`DATA_LENGTH` + `information_schema`.`TABLES`.`INDEX_LENGTH`), FROM information_schema.SCHEMATA s
0
) AS UNSIGNED INTEGER) AS `size_bytes`
FROM `information_schema`.`SCHEMATA`
LEFT OUTER JOIN `information_schema`.`TABLES`
ON `information_schema`.`SCHEMATA`.`SCHEMA_NAME` = `TABLES`.`TABLE_SCHEMA`
LEFT OUTER JOIN `mysql`.`db`
ON `information_schema`.`SCHEMATA`.`SCHEMA_NAME` = `mysql`.`db`.`DB`
WHERE `information_schema`.`SCHEMATA`.`SCHEMA_NAME` = ?
GROUP BY `information_schema`.`SCHEMATA`.`SCHEMA_NAME`
",
LEFT JOIN (
SELECT
TABLE_SCHEMA,
GROUP_CONCAT(
DISTINCT CAST(TABLE_NAME AS CHAR(64))
ORDER BY TABLE_NAME
SEPARATOR ','
) AS tables,
SUM(DATA_LENGTH + INDEX_LENGTH) AS size_bytes
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = ?
GROUP BY TABLE_SCHEMA
) t
ON t.TABLE_SCHEMA = s.SCHEMA_NAME
LEFT JOIN (
SELECT
DB,
GROUP_CONCAT(
DISTINCT CAST(User AS CHAR(64))
ORDER BY User
SEPARATOR ','
) AS users
FROM mysql.db
WHERE DB = ?
GROUP BY DB
) u
ON u.DB = s.SCHEMA_NAME
WHERE s.SCHEMA_NAME = ?;
",
) )
.bind(database_name.to_string()) .bind(database_name.to_string())
.bind(database_name.to_string())
.bind(database_name.to_string())
.fetch_optional(&mut *connection) .fetch_optional(&mut *connection)
.await .await
.map_err(|err| ListDatabasesError::MySqlError(err.to_string())) .map_err(|err| ListDatabasesError::MySqlError(err.to_string()))
@@ -320,26 +343,57 @@ pub async fn list_all_databases_for_user(
let result = sqlx::query_as::<_, DatabaseRow>( let result = sqlx::query_as::<_, DatabaseRow>(
r" r"
SELECT SELECT
CAST(`information_schema`.`SCHEMATA`.`SCHEMA_NAME` AS CHAR(64)) AS `database`, CAST(s.SCHEMA_NAME AS CHAR(64)) AS `database`,
GROUP_CONCAT(DISTINCT CAST(`information_schema`.`TABLES`.`TABLE_NAME` AS CHAR(64)) SEPARATOR ',') AS `tables`, t.tables,
GROUP_CONCAT(DISTINCT CAST(`mysql`.`db`.`User` AS CHAR(64)) SEPARATOR ',') AS `users`, u.users,
MAX(`information_schema`.`SCHEMATA`.`DEFAULT_COLLATION_NAME`) AS `collation`, s.DEFAULT_COLLATION_NAME AS collation,
MAX(`information_schema`.`SCHEMATA`.`DEFAULT_CHARACTER_SET_NAME`) AS `character_set`, s.DEFAULT_CHARACTER_SET_NAME AS character_set,
CAST(IFNULL( CAST(COALESCE(t.size_bytes, 0) AS UNSIGNED) AS size_bytes
SUM(`information_schema`.`TABLES`.`DATA_LENGTH` + `information_schema`.`TABLES`.`INDEX_LENGTH`), FROM information_schema.SCHEMATA s
0
) AS UNSIGNED INTEGER) AS `size_bytes` LEFT JOIN (
FROM `information_schema`.`SCHEMATA` SELECT
LEFT OUTER JOIN `information_schema`.`TABLES` TABLE_SCHEMA,
ON `information_schema`.`SCHEMATA`.`SCHEMA_NAME` = `TABLES`.`TABLE_SCHEMA` GROUP_CONCAT(
LEFT OUTER JOIN `mysql`.`db` DISTINCT CAST(TABLE_NAME AS CHAR(64))
ON `information_schema`.`SCHEMATA`.`SCHEMA_NAME` = `mysql`.`db`.`DB` ORDER BY TABLE_NAME
WHERE `information_schema`.`SCHEMATA`.`SCHEMA_NAME` NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys') SEPARATOR ','
AND `information_schema`.`SCHEMATA`.`SCHEMA_NAME` REGEXP ? ) AS tables,
GROUP BY `information_schema`.`SCHEMATA`.`SCHEMA_NAME` SUM(DATA_LENGTH + INDEX_LENGTH) AS size_bytes
FROM information_schema.TABLES
WHERE TABLE_SCHEMA REGEXP ?
GROUP BY TABLE_SCHEMA
) t
ON t.TABLE_SCHEMA = s.SCHEMA_NAME
LEFT JOIN (
SELECT
DB,
GROUP_CONCAT(
DISTINCT CAST(User AS CHAR(64))
ORDER BY User
SEPARATOR ','
) AS users
FROM mysql.db
WHERE DB REGEXP ?
GROUP BY DB
) u
ON u.DB = s.SCHEMA_NAME
WHERE s.SCHEMA_NAME REGEXP ?
AND s.SCHEMA_NAME NOT IN (
'information_schema',
'performance_schema',
'mysql',
'sys'
)
ORDER BY s.SCHEMA_NAME
", ",
) )
.bind(create_user_group_matching_regex(unix_user, group_denylist)) .bind(create_user_group_matching_regex(unix_user, group_denylist))
.bind(create_user_group_matching_regex(unix_user, group_denylist))
.bind(create_user_group_matching_regex(unix_user, group_denylist))
.fetch_all(connection) .fetch_all(connection)
.await .await
.map_err(|err| ListAllDatabasesError::MySqlError(err.to_string())); .map_err(|err| ListAllDatabasesError::MySqlError(err.to_string()));