From 23df8ffd752d90c6fa3e86102a8ed3e7bafdf08c Mon Sep 17 00:00:00 2001 From: "man.zhong" Date: Wed, 11 Mar 2026 09:27:19 +0800 Subject: [PATCH] feat(g3fo-db-ops): replace MCP MySQL with Python db_ops tool, add connection timeout Made-with: Cursor --- skills/g3fo-db-ops/SKILL.md | 107 +++- .../DATABASE_UPDATE_AUTOMATION_GUIDE.md | 32 +- skills/g3fo-db-ops/scripts/README.md | 58 +++ skills/g3fo-db-ops/scripts/db_ops.py | 462 ++++++++++++++++++ skills/g3fo-db-ops/scripts/requirements.txt | 1 + 5 files changed, 634 insertions(+), 26 deletions(-) create mode 100644 skills/g3fo-db-ops/scripts/README.md create mode 100644 skills/g3fo-db-ops/scripts/db_ops.py create mode 100644 skills/g3fo-db-ops/scripts/requirements.txt diff --git a/skills/g3fo-db-ops/SKILL.md b/skills/g3fo-db-ops/SKILL.md index 5b6a3f2..cc69f24 100644 --- a/skills/g3fo-db-ops/SKILL.md +++ b/skills/g3fo-db-ops/SKILL.md @@ -1,6 +1,6 @@ --- name: g3fo-db-ops -description: Comprehensive database operations guide for G3FO project. Use when updating G3FO database data, creating tables, modifying table structures, inserting system menus/routes, or managing database changes that require SQL generation, execution, and Git commit workflows. Includes automated workflows for i18n updates, version management, file naming conventions, and duplicate sequence number detection. +description: Comprehensive database operations guide for G3FO project. Use when updating G3FO database data, creating tables, modifying table structures, inserting system menus/routes, or managing database changes that require SQL generation, execution, and Git commit workflows. Includes automated workflows for i18n updates, version management, file naming conventions, and duplicate sequence number detection. Uses Python db_ops tool (not MCP MySQL) for database operations. --- # G3FO Database Operations @@ -9,6 +9,85 @@ Guide for performing database operations in the G3FO project, including automate **Detailed documentation**: See `references/DATABASE_UPDATE_AUTOMATION_GUIDE.md` for complete automation guide. +## Database Tool (db_ops.py) + +**本技能不再使用 MCP MySQL**,改用 Python 程序 `scripts/db_ops.py` 执行所有数据库操作。 + +### 安装依赖 + +```bash +pip install -r .claude/skills/g3fo-db-ops/scripts/requirements.txt +``` + +### 默认连接配置 + +与 MCP user-mysql 一致,未指定时自动使用: + +| 参数 | 默认值 | +|------|--------| +| host | 192.168.3.233 | +| port | 3306 | +| user | root | +| password | afe123456 | +| database | g3fo_base | + +**切换数据库**:系统表用 `g3fo_base`,交易相关用 `g3fo_trade`。通过 `--database g3fo_trade` 或环境变量 `MYSQL_DATABASE` 覆盖。 + +### 覆盖连接方式 + +1. **命令行参数**:`--host`, `--port`, `--user`, `--password`, `--database` +2. **环境变量**:`MYSQL_HOST`, `MYSQL_PORT`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_DATABASE` + +### 命令列表 + +| 命令 | 说明 | +|------|------| +| `query` | 执行 SELECT 查询 | +| `execute` | 执行 INSERT/UPDATE/DELETE | +| `list_tables` | 列举数据库所有表 | +| `describe_table` | 获取表结构(列信息、表注释) | +| `call_procedure` | 执行存储过程 | +| `batch_execute` | 批量执行 SQL(支持文件、DELIMITER) | +| `get_table_comment` | 获取表注释 | +| `set_table_comment` | 修改表注释 | +| `get_column_comment` | 获取列注释 | +| `set_column_comment` | 修改列注释 | +| `test_connection` | 测试连接 | + +### 使用示例 + +```bash +# 从 skill 根目录或 server 根目录执行,base_dir 为 .claude/skills/g3fo-db-ops 所在路径 + +# 测试连接 +python scripts/db_ops.py test_connection +python scripts/db_ops.py --database g3fo_trade test_connection + +# 查询 +python scripts/db_ops.py query "SELECT * FROM m_system_code LIMIT 5" + +# 执行增删改 +python scripts/db_ops.py execute "REPLACE INTO g3fo_base.m_system_code (...) VALUES (...)" + +# 列举所有表 +python scripts/db_ops.py list_tables +python scripts/db_ops.py --database g3fo_trade list_tables + +# 获取表结构 +python scripts/db_ops.py describe_table m_system_code + +# 执行存储过程 +python scripts/db_ops.py call_procedure InsertSystemMenu --params "菜单备注" "menu-name" "title_remark" "ADMIN" + +# 批量执行 SQL 文件 +python scripts/db_ops.py batch_execute --file path/to/script.sql + +# 修改表注释 +python scripts/db_ops.py set_table_comment m_system_code "系统编码表" +``` + +**在 Cursor 中调用时**:使用 `run_terminal_cmd` 或 Shell 工具执行上述命令,工作目录为 `d:\AFE Git\G3SF\G3FO\server`,脚本路径为 `.claude/skills/g3fo-db-ops/scripts/db_ops.py`。 + ## Core Principles 1. **Use existing templates**: Prioritize templates and tools in `g3fo-db/common_sql/` @@ -72,16 +151,16 @@ Display generated SQL and ask: ### 4. SQL Execution Phase If user confirms: -1. Connect to database using MySQL MCP - - **Default database connection** (automatically use when MySQL MCP or database connection is needed): - - host: 192.168.3.233 - - user: root - - password: afe123456 - - database: g3fo_base (for system tables like m_system_code, m_system_i18n, m_system_error_message, etc.) - - database: g3fo_trade (for trade-related tables) -2. Execute SQL -3. Verify results -4. If failed, report error and stop +1. Use **db_ops.py** to execute SQL (run in terminal): + - **Default connection** (see "Database Tool" section above): host 192.168.3.233, user root, password afe123456 + - System tables: `--database g3fo_base` + - Trade tables: `--database g3fo_trade` + - Override via `--host`, `--port`, `--user`, `--password`, `--database` if user provides different connection info +2. For single SELECT: `python .claude/skills/g3fo-db-ops/scripts/db_ops.py query "SQL"` +3. For INSERT/UPDATE/DELETE: `python .claude/skills/g3fo-db-ops/scripts/db_ops.py execute "SQL"` +4. For batch SQL or file: `python .claude/skills/g3fo-db-ops/scripts/db_ops.py batch_execute --file path/to/file.sql` +5. Verify results (check JSON output for `"success": true`) +6. If failed, report error and stop ### 5. Git Commit Phase @@ -195,9 +274,9 @@ If confirmed, **execute in this order** to ensure remote repository and local da **Execution and commit rules:** 1. **SQL execution phase**: - - MySQL MCP doesn't support `DELIMITER` for stored procedures - - Can use direct `INSERT INTO` or `REPLACE INTO` to insert same data - - Ensure data matches stored procedure output exactly + - Use **db_ops.py** `batch_execute --file` for SQL files with `DELIMITER` (script handles it) + - Or use `call_procedure` for stored procedure calls + - Or use `execute` with direct `INSERT INTO` / `REPLACE INTO` if data matches procedure output 2. **Git commit phase**: - **Must use complete SQL from stored procedure file** diff --git a/skills/g3fo-db-ops/references/DATABASE_UPDATE_AUTOMATION_GUIDE.md b/skills/g3fo-db-ops/references/DATABASE_UPDATE_AUTOMATION_GUIDE.md index 4f8b70e..0cf222c 100644 --- a/skills/g3fo-db-ops/references/DATABASE_UPDATE_AUTOMATION_GUIDE.md +++ b/skills/g3fo-db-ops/references/DATABASE_UPDATE_AUTOMATION_GUIDE.md @@ -32,14 +32,20 @@ g3fo-db/ #### 执行 SQL 阶段 -由于 MySQL MCP 不支持使用 `DELIMITER` 的存储过程,执行时可以使用其他方法插入或更新相同的数据: +使用 **db_ops.py**(Python 数据库工具,替代 MCP MySQL)执行 SQL: -1. **使用直接的 INSERT/REPLACE 语句**: - - 使用 `INSERT INTO` 或 `REPLACE INTO` 语句直接插入数据 +1. **批量执行含 DELIMITER 的存储过程文件**: + - 使用 `python scripts/db_ops.py batch_execute --file path/to/file.sql` + - db_ops.py 支持 DELIMITER,可正确解析并执行存储过程 + +2. **或使用 call_procedure 调用已存在的存储过程**: + - `python scripts/db_ops.py call_procedure InsertSystemMenu --params "..." "..." "..."` + +3. **或使用直接的 INSERT/REPLACE 语句**: + - 使用 `execute` 命令执行 `INSERT INTO` 或 `REPLACE INTO` - 确保数据与存储过程生成的数据完全一致 - - 需要手动处理 ID 生成、权限记录创建等逻辑 -2. **数据一致性要求**: +4. **数据一致性要求**: - 菜单数据必须插入到 `m_system_menus` 表 - 路由数据必须插入到 `m_system_routes` 表 - 相应的权限记录必须插入到 `m_admin_role_rights` 或 `m_user_role_rights` 表 @@ -328,12 +334,13 @@ Cursor 自动执行以下操作: #### 步骤 4: 执行SQL -如果用户确认无误,使用 MySQL MCP 执行 SQL 语句: +如果用户确认无误,使用 **db_ops.py** 执行 SQL 语句: -1. 连接到数据库 -2. 执行 SQL 语句 -3. 验证执行结果 -4. 如果成功,继续下一步;如果失败,报告错误并停止 +1. 在终端执行:`python .claude/skills/g3fo-db-ops/scripts/db_ops.py execute "SQL"` 或 `batch_execute --file path` +2. 默认连接:host 192.168.3.233, user root, password afe123456;可通过 `--database g3fo_base` 或 `g3fo_trade` 指定库 +3. 用户可提供不同连接信息时,使用 `--host`, `--port`, `--user`, `--password`, `--database` 覆盖 +4. 验证执行结果(JSON 输出中 `"success": true`) +5. 如果成功,继续下一步;如果失败,报告错误并停止 #### 步骤 5: 询问是否提交到Git @@ -764,8 +771,9 @@ feat: {简短描述} **重要:增加菜单和路径权限必须使用存储过程文件** 1. **执行 SQL 阶段**: - - 由于 MySQL MCP 不支持 `DELIMITER`,可以使用直接的 `INSERT INTO` 或 `REPLACE INTO` 语句执行 - - 确保数据与存储过程生成的数据完全一致 + - 使用 **db_ops.py** 的 `batch_execute --file` 执行含 DELIMITER 的存储过程文件 + - 或使用 `call_procedure` 调用已存在的存储过程 + - 或使用 `execute` 配合直接的 `INSERT INTO` / `REPLACE INTO`,确保数据与存储过程生成的数据完全一致 2. **提交到 Git 阶段**: - **必须使用存储过程文件生成的完整 SQL** diff --git a/skills/g3fo-db-ops/scripts/README.md b/skills/g3fo-db-ops/scripts/README.md new file mode 100644 index 0000000..04e6969 --- /dev/null +++ b/skills/g3fo-db-ops/scripts/README.md @@ -0,0 +1,58 @@ +# G3FO Database Operations Script + +替代 MCP MySQL 的 Python 数据库操作工具。 + +## 连接管理(无内存泄漏) + +- **单次执行**:每次命令是独立进程,执行完即退出 +- **连接生命周期**:连接仅在单次命令执行期间存在(通常几毫秒到几秒),`finally` 中统一 `conn.close()` +- **无长连接/连接池**:用完即关,不会累积连接 +- **连接超时**:默认 10 秒,防止网络慢时长时间挂起 + +## 安装 + +```bash +pip install -r requirements.txt +# 或 +pip install mysql-connector-python +``` + +## 默认连接 + +与 MCP user-mysql 一致: +- host: 192.168.3.233 +- port: 3306 +- user: root +- password: afe123456 +- database: g3fo_base + +## 覆盖连接 + +- 命令行:`--host`, `--port`, `--user`, `--password`, `--database`, `--connection-timeout` +- 环境变量:`MYSQL_HOST`, `MYSQL_PORT`, `MYSQL_USER`, `MYSQL_PASSWORD`, `MYSQL_DATABASE`, `MYSQL_CONNECTION_TIMEOUT` + +## 命令 + +| 命令 | 说明 | +|------|------| +| query | SELECT 查询 | +| execute | INSERT/UPDATE/DELETE | +| list_tables | 列举所有表 | +| describe_table | 表结构 | +| call_procedure | 执行存储过程 | +| batch_execute | 批量 SQL(支持文件) | +| get_table_comment | 获取表注释 | +| set_table_comment | 修改表注释 | +| get_column_comment | 获取列注释 | +| set_column_comment | 修改列注释 | +| test_connection | 测试连接 | + +## 示例 + +```bash +python db_ops.py test_connection +python db_ops.py query "SELECT 1" +python db_ops.py --database g3fo_trade list_tables +python db_ops.py describe_table m_order +python db_ops.py batch_execute --file script.sql +``` diff --git a/skills/g3fo-db-ops/scripts/db_ops.py b/skills/g3fo-db-ops/scripts/db_ops.py new file mode 100644 index 0000000..31fffe9 --- /dev/null +++ b/skills/g3fo-db-ops/scripts/db_ops.py @@ -0,0 +1,462 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +G3FO Database Operations Tool + +替代 MCP MySQL,提供完整的数据库操作能力: +- 增删改查 (query, execute) +- 列举所有表 (list_tables) +- 执行存储过程 (call_procedure) +- 批量执行 SQL (batch_execute) +- 读取表结构 (describe_table) +- 读取/修改表和列注释 (get/set comments) + +默认连接配置与 MCP user-mysql 一致,可通过参数或环境变量覆盖。 +""" + +import argparse +import json +import os +import sys +from typing import Any, Optional + +try: + import mysql.connector + from mysql.connector import Error as MySQLError +except ImportError: + print("Error: mysql-connector-python is required. Run: pip install mysql-connector-python", file=sys.stderr) + sys.exit(1) + +# 默认连接配置(与 MCP user-mysql 一致) +DEFAULT_CONFIG = { + "host": "192.168.3.233", + "port": 3306, + "user": "root", + "password": "afe123456", + "database": "g3fo_base", + "connection_timeout": 10, # 连接超时(秒),防止长时间挂起 +} + +ENV_MAPPING = { + "host": "MYSQL_HOST", + "port": "MYSQL_PORT", + "user": "MYSQL_USER", + "password": "MYSQL_PASSWORD", + "database": "MYSQL_DATABASE", + "connection_timeout": "MYSQL_CONNECTION_TIMEOUT", +} + + +def get_connection_config( + host: Optional[str] = None, + port: Optional[int] = None, + user: Optional[str] = None, + password: Optional[str] = None, + database: Optional[str] = None, + connection_timeout: Optional[int] = None, +) -> dict: + """从默认配置、环境变量、参数中合并连接配置,参数优先级最高。""" + config = {} + params = {"host": host, "port": port, "user": user, "password": password, "database": database, "connection_timeout": connection_timeout} + for key, default in DEFAULT_CONFIG.items(): + env_key = ENV_MAPPING.get(key) + env_val = os.environ.get(env_key) if env_key else None + param_val = params.get(key) + if param_val is not None: + config[key] = int(param_val) if key in ("port", "connection_timeout") else param_val + elif env_val is not None: + config[key] = int(env_val) if key in ("port", "connection_timeout") else env_val + else: + config[key] = default + return config + + +def get_connection(config: dict): + """ + 创建数据库连接。 + 连接仅在单次命令执行期间存在,命令结束或异常时在 finally 中关闭,不会长期占用。 + """ + return mysql.connector.connect( + host=config["host"], + port=config["port"], + user=config["user"], + password=config["password"], + database=config["database"], + charset="utf8mb4", + collation="utf8mb4_unicode_ci", + connection_timeout=config.get("connection_timeout", 10), + ) + + +def cmd_query(args: argparse.Namespace, config: dict) -> dict: + """执行 SELECT 查询。""" + conn = get_connection(config) + try: + cursor = conn.cursor(dictionary=True) + cursor.execute(args.sql, args.params or []) + rows = cursor.fetchall() + cursor.close() + return {"success": True, "data": rows, "rowCount": len(rows)} + except MySQLError as e: + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_execute(args: argparse.Namespace, config: dict) -> dict: + """执行 INSERT/UPDATE/DELETE。""" + conn = get_connection(config) + try: + cursor = conn.cursor() + cursor.execute(args.sql, args.params or []) + conn.commit() + affected = cursor.rowcount + cursor.close() + return {"success": True, "affectedRows": affected} + except MySQLError as e: + conn.rollback() + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_list_tables(args: argparse.Namespace, config: dict) -> dict: + """列举数据库中的所有表。""" + conn = get_connection(config) + try: + cursor = conn.cursor() + cursor.execute( + "SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, TABLE_COMMENT " + "FROM information_schema.TABLES WHERE TABLE_SCHEMA = %s ORDER BY TABLE_NAME", + (config["database"],), + ) + rows = cursor.fetchall() + cursor.close() + tables = [ + { + "schema": r[0], + "name": r[1], + "type": r[2], + "comment": r[3] or "", + } + for r in rows + ] + return {"success": True, "tables": tables, "count": len(tables)} + except MySQLError as e: + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_describe_table(args: argparse.Namespace, config: dict) -> dict: + """获取表结构(列信息)。""" + conn = get_connection(config) + try: + cursor = conn.cursor() + table = args.table + schema = args.schema or config["database"] + cursor.execute( + """ + SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_DEFAULT, COLUMN_COMMENT, + EXTRA + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s + ORDER BY ORDINAL_POSITION + """, + (schema, table), + ) + rows = cursor.fetchall() + cursor.close() + columns = [ + { + "name": r[0], + "type": r[1], + "nullable": r[2], + "key": r[3] or "", + "default": r[4], + "comment": r[5] or "", + "extra": r[6] or "", + } + for r in rows + ] + # 获取表注释 + cursor = conn.cursor() + cursor.execute( + "SELECT TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s", + (schema, table), + ) + tbl = cursor.fetchone() + table_comment = tbl[0] if tbl else "" + cursor.close() + return {"success": True, "table": table, "schema": schema, "columns": columns, "tableComment": table_comment} + except MySQLError as e: + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_call_procedure(args: argparse.Namespace, config: dict) -> dict: + """执行存储过程。""" + conn = get_connection(config) + try: + cursor = conn.cursor() + placeholders = ", ".join(["%s"] * len(args.params)) if args.params else "" + sql = f"CALL {args.procedure}({placeholders})" if placeholders else f"CALL {args.procedure}()" + cursor.execute(sql, args.params or []) + rows = [] + if cursor.description: + rows = cursor.fetchall() + while cursor.nextset(): + if cursor.description: + rows.extend(cursor.fetchall()) + conn.commit() + cursor.close() + return {"success": True, "data": rows, "rowCount": len(rows)} + except MySQLError as e: + conn.rollback() + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_batch_execute(args: argparse.Namespace, config: dict) -> dict: + """批量执行 SQL(支持多条语句、存储过程、DELIMITER)。""" + sql_text = args.sql or "" + if args.file: + with open(args.file, "r", encoding="utf-8") as f: + sql_text = f.read() + if not sql_text.strip(): + return {"success": False, "error": "No SQL provided. Use 'sql' argument or --file"} + # 处理 DELIMITER(存储过程等):移除 DELIMITER 行,将 $$ 替换为 ; + if "DELIMITER" in sql_text.upper(): + lines = [] + for line in sql_text.split("\n"): + if line.strip().upper().startswith("DELIMITER"): + continue + lines.append(line.replace("$$", ";")) + sql_text = "\n".join(lines) + conn = get_connection(config) + results = [] + try: + cursor = conn.cursor() + # mysql-connector-python 9.2+ 已移除 multi 参数,直接 execute 即可执行多条语句 + cursor.execute(sql_text) + # 消费所有结果集(SELECT 返回行、DML 返回 affected rows) + while True: + if cursor.description: + results.append({"type": "query", "rowCount": len(cursor.fetchall())}) + else: + results.append({"type": "execute", "affectedRows": cursor.rowcount}) + if not cursor.nextset(): + break + conn.commit() + cursor.close() + return {"success": True, "results": results} + except MySQLError as e: + conn.rollback() + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_get_table_comment(args: argparse.Namespace, config: dict) -> dict: + """获取表注释。""" + conn = get_connection(config) + try: + cursor = conn.cursor() + schema = args.schema or config["database"] + cursor.execute( + "SELECT TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s", + (schema, args.table), + ) + row = cursor.fetchone() + cursor.close() + return {"success": True, "table": args.table, "schema": schema, "comment": row[0] if row else ""} + except MySQLError as e: + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_set_table_comment(args: argparse.Namespace, config: dict) -> dict: + """修改表注释。""" + conn = get_connection(config) + try: + cursor = conn.cursor() + schema = args.schema or config["database"] + sql = f"ALTER TABLE `{schema}`.`{args.table}` COMMENT = %s" + cursor.execute(sql, (args.comment,)) + conn.commit() + cursor.close() + return {"success": True, "table": args.table, "schema": schema} + except MySQLError as e: + conn.rollback() + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_get_column_comment(args: argparse.Namespace, config: dict) -> dict: + """获取列注释。""" + conn = get_connection(config) + try: + cursor = conn.cursor() + schema = args.schema or config["database"] + cursor.execute( + "SELECT COLUMN_COMMENT FROM information_schema.COLUMNS " + "WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s", + (schema, args.table, args.column), + ) + row = cursor.fetchone() + cursor.close() + return {"success": True, "table": args.table, "column": args.column, "comment": row[0] if row else ""} + except MySQLError as e: + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_set_column_comment(args: argparse.Namespace, config: dict) -> dict: + """修改列注释(需要 COLUMN_TYPE,可通过 describe_table 获取)。""" + conn = get_connection(config) + try: + cursor = conn.cursor() + schema = args.schema or config["database"] + cursor.execute( + "SELECT COLUMN_TYPE FROM information_schema.COLUMNS " + "WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s", + (schema, args.table, args.column), + ) + row = cursor.fetchone() + if not row: + cursor.close() + return {"success": False, "error": f"Column {args.column} not found"} + col_type = row[0] + sql = f"ALTER TABLE `{schema}`.`{args.table}` MODIFY COLUMN `{args.column}` {col_type} COMMENT %s" + cursor.execute(sql, (args.comment,)) + conn.commit() + cursor.close() + return {"success": True, "table": args.table, "column": args.column} + except MySQLError as e: + conn.rollback() + return {"success": False, "error": str(e)} + finally: + conn.close() + + +def cmd_test_connection(args: argparse.Namespace, config: dict) -> dict: + """测试数据库连接。""" + conn = None + try: + conn = get_connection(config) + cursor = conn.cursor() + cursor.execute("SELECT 1 AS conn_ok") + row = cursor.fetchone() + cursor.close() + return {"success": True, "message": "Connection OK", "conn_ok": row[0] if row else 1} + except MySQLError as e: + return {"success": False, "error": str(e)} + finally: + if conn: + conn.close() + + +def main(): + parser = argparse.ArgumentParser(description="G3FO Database Operations Tool") + parser.add_argument("--host", help="MySQL host (default: 192.168.3.233)") + parser.add_argument("--port", type=int, help="MySQL port (default: 3306)") + parser.add_argument("--user", help="MySQL user (default: root)") + parser.add_argument("--password", help="MySQL password") + parser.add_argument("--database", help="MySQL database (default: g3fo_base)") + parser.add_argument("--connection-timeout", type=int, dest="connection_timeout", help="Connection timeout in seconds (default: 10)") + + + subparsers = parser.add_subparsers(dest="command", required=True) + + # query + p_query = subparsers.add_parser("query", help="Execute SELECT query") + p_query.add_argument("sql", help="SQL SELECT statement") + p_query.add_argument("--params", nargs="*", help="Query parameters") + + # execute + p_execute = subparsers.add_parser("execute", help="Execute INSERT/UPDATE/DELETE") + p_execute.add_argument("sql", help="SQL statement") + p_execute.add_argument("--params", nargs="*", help="Query parameters") + + # list_tables + subparsers.add_parser("list_tables", help="List all tables in database") + + # describe_table + p_desc = subparsers.add_parser("describe_table", help="Get table structure") + p_desc.add_argument("table", help="Table name") + p_desc.add_argument("--schema", help="Schema/database (default: current database)") + + # call_procedure + p_proc = subparsers.add_parser("call_procedure", help="Execute stored procedure") + p_proc.add_argument("procedure", help="Procedure name (e.g. InsertSystemMenu)") + p_proc.add_argument("--params", nargs="*", help="Procedure parameters") + + # batch_execute + p_batch = subparsers.add_parser("batch_execute", help="Execute batch SQL") + p_batch.add_argument("sql", nargs="?", help="SQL text (multiple statements separated by ;)") + p_batch.add_argument("--file", "-f", help="SQL file path") + p_batch.add_argument("--continue-on-error", action="store_true", dest="continue_on_error", help="Continue on error") + + # get_table_comment + p_gtc = subparsers.add_parser("get_table_comment", help="Get table comment") + p_gtc.add_argument("table", help="Table name") + p_gtc.add_argument("--schema", help="Schema (default: current database)") + + # set_table_comment + p_stc = subparsers.add_parser("set_table_comment", help="Set table comment") + p_stc.add_argument("table", help="Table name") + p_stc.add_argument("comment", help="Comment text") + p_stc.add_argument("--schema", help="Schema (default: current database)") + + # get_column_comment + p_gcc = subparsers.add_parser("get_column_comment", help="Get column comment") + p_gcc.add_argument("table", help="Table name") + p_gcc.add_argument("column", help="Column name") + p_gcc.add_argument("--schema", help="Schema (default: current database)") + + # set_column_comment + p_scc = subparsers.add_parser("set_column_comment", help="Set column comment") + p_scc.add_argument("table", help="Table name") + p_scc.add_argument("column", help="Column name") + p_scc.add_argument("comment", help="Comment text") + p_scc.add_argument("--schema", help="Schema (default: current database)") + + # test_connection + subparsers.add_parser("test_connection", help="Test database connection") + + args = parser.parse_args() + config = get_connection_config( + host=args.host, + port=args.port, + user=args.user, + password=args.password, + database=args.database, + connection_timeout=getattr(args, "connection_timeout", None), + ) + + commands = { + "query": cmd_query, + "execute": cmd_execute, + "list_tables": cmd_list_tables, + "describe_table": cmd_describe_table, + "call_procedure": cmd_call_procedure, + "batch_execute": cmd_batch_execute, + "get_table_comment": cmd_get_table_comment, + "set_table_comment": cmd_set_table_comment, + "get_column_comment": cmd_get_column_comment, + "set_column_comment": cmd_set_column_comment, + "test_connection": cmd_test_connection, + } + handler = commands[args.command] + result = handler(args, config) + print(json.dumps(result, ensure_ascii=False, indent=2)) + sys.exit(0 if result.get("success", False) else 1) + + +if __name__ == "__main__": + main() diff --git a/skills/g3fo-db-ops/scripts/requirements.txt b/skills/g3fo-db-ops/scripts/requirements.txt new file mode 100644 index 0000000..a59bd92 --- /dev/null +++ b/skills/g3fo-db-ops/scripts/requirements.txt @@ -0,0 +1 @@ +mysql-connector-python>=8.0.0