|
16 | 16 | import time |
17 | 17 | from datetime import datetime |
18 | 18 | from typing import Dict, Any, List, Optional |
| 19 | +from pydantic import Field |
19 | 20 |
|
20 | 21 | from alibabacloud_bssopenapi20171214 import models as bss_open_api_20171214_models |
21 | 22 | from alibabacloud_openapi_util.client import Client as OpenApiUtilClient |
| 23 | +from alibabacloud_rds20140815.client import Client as RdsClient |
22 | 24 | from alibabacloud_rds20140815 import models as rds_20140815_models |
23 | 25 | from alibabacloud_tea_openapi import models as open_api_models |
24 | 26 | from alibabacloud_tea_util import models as util_models |
@@ -1642,11 +1644,111 @@ async def query_sql( |
1642 | 1644 | the sql result. |
1643 | 1645 | """ |
1644 | 1646 | try: |
1645 | | - async with DBService(region_id, dbinstance_id, db_name) as service: |
| 1647 | + if db_name == 'information_schema': |
| 1648 | + client = get_rds_client(region_id) |
| 1649 | + databases = _get_db_instance_databases_str(client, region_id, dbinstance_id) |
| 1650 | + else: |
| 1651 | + databases = None |
| 1652 | + async with DBService(region_id, dbinstance_id, db_name, databases) as service: |
1646 | 1653 | return await service.execute_sql(sql=sql) |
1647 | 1654 | except Exception as e: |
1648 | 1655 | logger.error(f"Error occurred: {str(e)}") |
1649 | 1656 | raise e |
| 1657 | + |
| 1658 | +@mcp.tool(annotations=READ_ONLY_TOOL, |
| 1659 | + description="Query the top few tables with the highest space occupancy") |
| 1660 | +async def show_largest_table( |
| 1661 | + region_id: str = Field(description="The region ID of the RDS instance."), |
| 1662 | + dbinstance_id: str = Field(description="The ID of the RDS instance."), |
| 1663 | + topK: int = Field(default=5, description="To display the number of the top few largest tables") |
| 1664 | +) -> str: |
| 1665 | + """ |
| 1666 | + Returns: |
| 1667 | + The data table with the largest storage space. |
| 1668 | + """ |
| 1669 | + try: |
| 1670 | + client = get_rds_client(region_id) |
| 1671 | + request = rds_20140815_models.DescribeDBInstanceAttributeRequest(dbinstance_id=dbinstance_id) |
| 1672 | + response = client.describe_dbinstance_attribute(request) |
| 1673 | + result = response.body.to_map() |
| 1674 | + |
| 1675 | + db_type = result['Items']['DBInstanceAttribute'][0]['Engine'] |
| 1676 | + db_version = result['Items']['DBInstanceAttribute'][0]['EngineVersion'] |
| 1677 | + if db_type.lower() != 'mysql' and db_version not in ["5.6", "5.7", "8.0"]: |
| 1678 | + return f"Unsupported db version {db_version}." |
| 1679 | + |
| 1680 | + db_name = 'information_schema' |
| 1681 | + databases = _get_db_instance_databases_str(client, region_id, dbinstance_id) |
| 1682 | + # 构建SQL |
| 1683 | + # 限制展示最大表数量的上限 |
| 1684 | + topK = min(topK, 100) |
| 1685 | + base_query = f""" |
| 1686 | + SELECT |
| 1687 | + table_schema AS '数据库', |
| 1688 | + table_name AS '表名', |
| 1689 | + ROUND((data_length + index_length) / 1024 / 1024, 2) AS '总大小(MB)', |
| 1690 | + ROUND(INDEX_LENGTH / 1024 / 1024, 2) AS '索引大小(MB)', |
| 1691 | + table_rows AS '行数' |
| 1692 | + FROM information_schema.TABLES |
| 1693 | + ORDER BY (data_length + index_length) DESC |
| 1694 | + Limit {topK}; |
| 1695 | + """ |
| 1696 | + |
| 1697 | + async with DBService(region_id, dbinstance_id, db_name, databases) as service: |
| 1698 | + return await service.execute_sql(sql=base_query) |
| 1699 | + except Exception as e: |
| 1700 | + logger.exception("show largest table failed.") |
| 1701 | + logger.error(f"Error occurred: {str(e)}") |
| 1702 | + raise e |
| 1703 | + |
| 1704 | + |
| 1705 | +@mcp.tool(annotations=READ_ONLY_TOOL, |
| 1706 | + description="Query the tables with the largest table fragments") |
| 1707 | +async def show_largest_table_fragment( |
| 1708 | + region_id: str = Field(description="The region ID of the RDS instance."), |
| 1709 | + dbinstance_id: str = Field(description="The ID of the RDS instance."), |
| 1710 | + topK: int = Field(default=5, description="To display the number of the top few largest tables") |
| 1711 | +) -> str: |
| 1712 | + """ |
| 1713 | + Returns: |
| 1714 | + The largest fragmented data table. |
| 1715 | + """ |
| 1716 | + try: |
| 1717 | + client = get_rds_client(region_id) |
| 1718 | + request = rds_20140815_models.DescribeDBInstanceAttributeRequest(dbinstance_id=dbinstance_id) |
| 1719 | + response = client.describe_dbinstance_attribute(request) |
| 1720 | + result = response.body.to_map() |
| 1721 | + |
| 1722 | + db_type = result['Items']['DBInstanceAttribute'][0]['Engine'] |
| 1723 | + db_version = result['Items']['DBInstanceAttribute'][0]['EngineVersion'] |
| 1724 | + if db_type.lower() != 'mysql' and db_version not in ["5.6", "5.7", "8.0"]: |
| 1725 | + return f"Unsupported db version {db_version}." |
| 1726 | + |
| 1727 | + db_name = 'information_schema' |
| 1728 | + databases = _get_db_instance_databases_str(client, region_id, dbinstance_id) |
| 1729 | + # 构建SQL |
| 1730 | + # 限制展示最大表数量的上限 |
| 1731 | + topK = min(topK, 100) |
| 1732 | + base_query = f""" |
| 1733 | + SELECT |
| 1734 | + ENGINE, |
| 1735 | + TABLE_SCHEMA, |
| 1736 | + TABLE_NAME, |
| 1737 | + ROUND(DATA_LENGTH / 1024 / 1024,2) AS '总大小(MB)', |
| 1738 | + ROUND(INDEX_LENGTH / 1024 / 1024,2) AS '索引大小(MB)', |
| 1739 | + ROUND(DATA_FREE / 1024 / 1024,2) AS '碎片大小(MB)' |
| 1740 | + FROM information_schema.TABLES |
| 1741 | + WHERE DATA_FREE > 0 |
| 1742 | + ORDER BY DATA_FREE DESC |
| 1743 | + Limit {topK}; |
| 1744 | + """ |
| 1745 | + |
| 1746 | + async with DBService(region_id, dbinstance_id, db_name, databases) as service: |
| 1747 | + return await service.execute_sql(sql=base_query) |
| 1748 | + except Exception as e: |
| 1749 | + logger.exception("show largest table failed.") |
| 1750 | + logger.error(f"Error occurred: {str(e)}") |
| 1751 | + raise e |
1650 | 1752 |
|
1651 | 1753 | async def health_check(request: Request) -> JSONResponse: |
1652 | 1754 | """Health check endpoint for container health monitoring and load balancer probes.""" |
@@ -1763,6 +1865,24 @@ def _parse_groups_from_source(source: str | None) -> List[str]: |
1763 | 1865 | expanded_groups.append(g_to_add) |
1764 | 1866 | return expanded_groups or [DEFAULT_TOOL_GROUP] |
1765 | 1867 |
|
| 1868 | +def _get_db_instance_databases_str( |
| 1869 | + client: RdsClient, |
| 1870 | + region_id: str, |
| 1871 | + db_instance_id: str |
| 1872 | +) -> str: |
| 1873 | + try: |
| 1874 | + client = get_rds_client(region_id) |
| 1875 | + request = rds_20140815_models.DescribeDatabasesRequest( |
| 1876 | + dbinstance_id=db_instance_id |
| 1877 | + ) |
| 1878 | + response = client.describe_databases(request) |
| 1879 | + databases_info = response.body.databases.database |
| 1880 | + |
| 1881 | + result = ",".join([database.dbname for database in databases_info]) |
| 1882 | + return result |
| 1883 | + except Exception as e: |
| 1884 | + raise e |
| 1885 | + |
1766 | 1886 |
|
1767 | 1887 | if __name__ == "__main__": |
1768 | 1888 | parser = argparse.ArgumentParser() |
|
0 commit comments