feat: add MySQL database support alongside SQL Server

This commit implements multi-database support, allowing the system to switch
between SQL Server and MySQL databases seamlessly.

## New Features
- Database type selection (SQL Server or MySQL) via configuration
- Automatic table name conversion between formats ([dbo].[table] → dbo_table)
- Automatic parameter placeholder handling (? for SQL Server, %s for MySQL)
- GUI settings tab now includes database type dropdown and MySQL configuration

## Database Abstraction Layer
- db/base_connection.py: Abstract base class for database connections
- db/sqlserver_connection.py: SQL Server implementation
- db/mysql_connection.py: MySQL implementation using mysql-connector-python
- db/connection_factory.py: Factory pattern for creating connections
- db/table_name_converter.py: Table name format conversion utility

## DAO Base Class
- db/base_dao.py: Base DAO with helper methods for SQL conversion and placeholders

## Updated Components
- config/schema.py: Extended with DatabaseType enum and MySQL/SQLServer config classes
- config/defaults.py: Added MySQL default configuration
- config/loader.py: Updated to handle new database structure
- db/connection.py: Refactored to use factory pattern and load user config
- All DAO files: Updated to inherit from BaseDAO with automatic conversion

## Dependencies
- Added mysql-connector-python>=8.0.0 to requirements.txt

## Configuration
To use MySQL, set db_type to "mysql" in config/user_settings.json:
{
  "database": {
    "db_type": "mysql",
    "mysql": {
      "host": "192.168.31.83",
      "port": 3306,
      "database": "BLD_DB",
      "username": "remote_user",
      "password": "3.1415926Beeke"
    }
  }
}

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Misaka
2026-02-09 21:50:54 +08:00
parent 04b99292ad
commit caac411e17
17 changed files with 1661 additions and 513 deletions

140
db/table_name_converter.py Normal file
View File

@@ -0,0 +1,140 @@
"""
表名转换工具
处理 SQL Server 和 MySQL 之间的表名格式转换
"""
import re
from typing import List
class TableNameConverter:
"""表名转换工具类"""
# 匹配 SQL Server 表名格式:[schema].[tablename] 或 [schema].[table name]
SQLSERVER_PATTERN = re.compile(r'\[([^\]]+)\]\.\[([^\]]+)\]')
@staticmethod
def to_mysql(table_name: str) -> str:
"""
将 SQL Server 表名格式转换为 MySQL 格式
SQL Server: [schema].[tablename] → MySQL: schema_tablename
SQL Server: tablename → MySQL: dbo_tablename (默认 dbo)
Args:
table_name: SQL Server 格式的表名
Returns:
MySQL 格式的表名
Examples:
>>> TableNameConverter.to_mysql('[dbo].[BIPUsers]')
'dbo_BIPUsers'
>>> TableNameConverter.to_mysql('DiscreteMaterialPlanData')
'dbo_DiscreteMaterialPlanData'
>>> TableNameConverter.to_mysql('[productionContractData].[26年压力表合同数据]')
'productionContractData_26年压力表合同数据'
"""
# 尝试匹配 [schema].[tablename] 格式
match = TableNameConverter.SQLSERVER_PATTERN.match(table_name.strip())
if match:
schema = match.group(1)
table = match.group(2)
return f"{schema}_{table}"
# 如果没有匹配到,使用默认 schema dbo
return f"dbo_{table_name}"
@staticmethod
def to_sqlserver(table_name: str) -> str:
"""
将 MySQL 表名格式转换为 SQL Server 格式
MySQL: schema_tablename → SQL Server: [schema].[tablename]
Args:
table_name: MySQL 格式的表名
Returns:
SQL Server 格式的表名
Examples:
>>> TableNameConverter.to_sqlserver('dbo_BIPUsers')
'[dbo].[BIPUsers]'
>>> TableNameConverter.to_sqlserver('productionContractData_26年压力表合同数据')
'[productionContractData].[26年压力表合同数据]'
"""
# 分割第一个下划线
parts = table_name.split('_', 1)
if len(parts) == 2:
schema = parts[0]
table = parts[1]
return f"[{schema}].[{table}]"
# 如果没有下划线,使用默认 schema dbo
return f"[dbo].[{table_name}]"
@staticmethod
def convert_sql(sql: str, db_type: str) -> str:
"""
批量转换 SQL 语句中的表名
Args:
sql: SQL 语句
db_type: 目标数据库类型 ('sqlserver''mysql')
Returns:
转换后的 SQL 语句
Examples:
>>> sql = "SELECT * FROM [dbo].[BIPUsers] WHERE ID = ?"
>>> TableNameConverter.convert_sql(sql, 'mysql')
'SELECT * FROM dbo_BIPUsers WHERE ID = ?'
"""
if db_type == 'mysql':
# SQL Server → MySQL
def replace_to_mysql(match):
schema = match.group(1)
table = match.group(2)
return f"{schema}_{table}"
result = TableNameConverter.SQLSERVER_PATTERN.sub(replace_to_mysql, sql)
return result
elif db_type == 'sqlserver':
# MySQL → SQL Server
# 首先查找可能的 MySQL 格式表名schema_table 格式)
# 这是一个简化版本,可能无法处理所有边缘情况
result = sql
# 查找单词字符_单词字符 的模式(可能是表名)
mysql_pattern = re.compile(r'\b([a-zA-Z_][a-zA-Z0-9_]*)_([a-zA-Z0-9_\u4e00-\u9fff]+)\b')
matches = mysql_pattern.findall(result)
for schema, table in set(matches):
mysql_name = f"{schema}_{table}"
sqlserver_name = f"[{schema}].[{table}]"
result = result.replace(mysql_name, sqlserver_name)
return result
return sql
@staticmethod
def extract_table_names(sql: str) -> List[str]:
"""
从 SQL 语句中提取所有表名
Args:
sql: SQL 语句
Returns:
表名列表
"""
tables = []
# 查找 SQL Server 格式
sqlserver_matches = TableNameConverter.SQLSERVER_PATTERN.findall(sql)
for schema, table in sqlserver_matches:
tables.append(f"{schema}_{table}")
# 查找可能的 MySQL 格式
mysql_pattern = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*_[a-zA-Z0-9_]+\b')
mysql_matches = mysql_pattern.findall(sql)
tables.extend(mysql_matches)
return list(set(tables))