!964 feat: 达梦DM8

Merge pull request !964 from dhb52/feature/db
This commit is contained in:
芋道源码
2024-05-04 00:57:27 +00:00
committed by Gitee
7 changed files with 4517 additions and 5738 deletions

View File

@ -42,9 +42,36 @@ docker compose exec sqlserver bash /tmp/create_schema.sh
### 1.5 DM 达梦
TODO 暂未支持
下载达梦docker镜像 https://download.dameng.com/eco/dm8/dm8_20230808_rev197096_x86_rh6_64_single.tar
## 2. MySQL 转换其它数据库
加载镜像文件在镜像tar文件所在目录运行
```Bash
docker load -i dm8_20230808_rev197096_x86_rh6_64_single.tar
````
在项目`sql/tools`目录下运行:
```Bash
docker compose up -d dm8
# 注意:启动完 sqlserver 后,需要手动再执行如下命令,因为 SQL Server 不支持初始化脚本
docker compose exec dm8 bash -c "exec /opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql"
exit
```
**注意**: `sql/dm/ruoyi-vue-pro-dm8.sql`文件编码必须为`GBK`或者`GBK`超集
暂不支持 MacBook Apple Silicon因为 达梦 官方没有提供 Apple Silicon 版本的 Docker 镜像。
## 2. 测试数据库docker容器的销毁重建
开发测试过程中有时候需要创建全新干净的数据库。由于测试数据docker容器采用数据卷volume挂载数据库实例的数据目录因此销毁数据需要停止容器后删除数据卷然后再重新创建容器。以postgres为例操作如下
```Bash
docker compose down postgres
docker volume rm ruoyi-vue-pro_postgres
```
## 3. MySQL 转换其它数据库
### 2.1 实现原理

View File

@ -134,6 +134,14 @@ class Convertor(ABC):
"""
pass
def gen_dual(self) -> str:
"""生成虚拟 dual 表
Returns:
str: 生成脚本, 默认返回空脚本, 表示当前数据库无需手工创建
"""
return ""
@staticmethod
def inserts(table_name: str, script_content: str) -> Generator:
PREFIX = f"INSERT INTO `{table_name}`"
@ -192,6 +200,17 @@ class Convertor(ABC):
)
)
dual = self.gen_dual()
if dual:
print(
f"""-- ----------------------------
-- Table structure for dual
-- ----------------------------
{dual}
"""
)
error_scripts = []
for table_sql in self.table_script_list:
ddl = DDLParser(table_sql.replace("`", "")).run()
@ -348,6 +367,12 @@ CREATE SEQUENCE {table_name}_seq
return script
def gen_dual(self) -> str:
return """DROP TABLE IF EXISTS dual;
CREATE TABLE dual
(
);"""
class OracleConvertor(Convertor):
def __init__(self, src):
@ -571,7 +596,7 @@ GO
return "\n".join(f"{script}\nGO" for script in self.index(ddl))
def gen_insert(self, table_name: str) -> str:
"""生成 insert 语句,以及根据最后的 insert id+1 生成 Sequence"""
"""生成 insert 语句"""
# 收集 `table_name` 对应的 insert 语句
inserts = []
@ -601,6 +626,126 @@ SET IDENTITY_INSERT {table_name.lower()} OFF
GO
COMMIT
GO
-- @formatter:on"""
return script
def gen_dual(self) -> str:
return """DROP TABLE IF EXISTS dual
GO
CREATE TABLE dual
(
id int NULL
)
GO
EXEC sp_addextendedproperty
'MS_Description', N'数据库连接的表',
'SCHEMA', N'dbo',
'TABLE', N'dual'
GO"""
class DM8Convertor(Convertor):
def __init__(self, src):
super().__init__(src, "DM8")
def translate_type(self, type: str, size: Optional[Union[int, Tuple[int]]]):
"""类型转换"""
type = type.lower()
if type == "varchar":
return f"varchar({size})"
if type == "int":
return "int"
if type == "bigint" or type == "bigint unsigned":
return "bigint"
if type == "datetime":
return "datetime"
if type == "bit":
return "bit"
if type in ("tinyint", "smallint"):
return "smallint"
if type == "text":
return "text"
if type == "blob":
return "blob"
if type == "mediumblob":
return "varchar(10240)"
if type == "decimal":
return (
f"decimal({','.join(str(s) for s in size)})" if len(size) else "decimal"
)
def gen_create(self, ddl) -> str:
"""生成 CREATE 语句"""
def generate_column(col):
name = col["name"].lower()
if name == "id":
return "id bigint NOT NULL PRIMARY KEY IDENTITY"
type = col["type"].lower()
full_type = self.translate_type(type, col["size"])
nullable = "NULL" if col["nullable"] else "NOT NULL"
default = f"DEFAULT {col['default']}" if col["default"] is not None else ""
return f"{name} {full_type} {default} {nullable}"
table_name = ddl["table_name"].lower()
columns = [f"{generate_column(col).strip()}" for col in ddl["columns"]]
field_def_list = ",\n ".join(columns)
script = f"""-- ----------------------------
-- Table structure for {table_name}
-- ----------------------------
CREATE TABLE {table_name} (
{field_def_list}
);"""
# oracle INSERT '' 不能通过 NOT NULL 校验
script = script.replace("DEFAULT '' NOT NULL", "DEFAULT '' NULL")
return script
def gen_index(self, ddl: Dict) -> str:
return "\n".join(f"{script};" for script in self.index(ddl))
def gen_comment(self, table_sql: str, table_name: str) -> str:
script = ""
for field, comment_string in self.filed_comments(table_sql):
script += (
f"COMMENT ON COLUMN {table_name}.{field} IS '{comment_string}';" + "\n"
)
table_comment = self.table_comment(table_sql)
if table_comment:
script += f"COMMENT ON TABLE {table_name} IS '{table_comment}';\n"
return script
def gen_pk(self, table_name: str) -> str:
"""生成主键定义"""
return ""
def gen_index(self, ddl: Dict) -> str:
return "\n".join(f"{script};" for script in self.index(ddl))
def gen_insert(self, table_name: str) -> str:
"""拷贝 INSERT 语句"""
inserts = list(Convertor.inserts(table_name, self.content))
## 生成 insert 脚本
script = ""
if inserts:
inserts_lines = "\n".join(inserts)
script += f"""\n\n-- ----------------------------
-- Records of {table_name.lower()}
-- ----------------------------
-- @formatter:off
SET IDENTITY_INSERT {table_name.lower()} ON;
{inserts_lines}
COMMIT;
SET IDENTITY_INSERT {table_name.lower()} OFF;
-- @formatter:on"""
return script
@ -612,7 +757,7 @@ def main():
"type",
type=str,
help="目标数据库类型",
choices=["postgres", "oracle", "sqlserver"],
choices=["postgres", "oracle", "sqlserver", "dm8"],
)
args = parser.parse_args()
@ -624,6 +769,8 @@ def main():
convertor = OracleConvertor(sql_file)
elif args.type == "sqlserver":
convertor = SQLServerConvertor(sql_file)
elif args.type == "dm8":
convertor = DM8Convertor(sql_file)
else:
raise NotImplementedError(f"不支持目标数据库类型: {args.type}")

View File

@ -4,6 +4,7 @@ volumes:
mysql: { }
postgres: { }
sqlserver: { }
dm8: { }
services:
mysql:
@ -69,3 +70,25 @@ services:
- ../sqlserver/ruoyi-vue-pro.sql:/tmp/schema.sql:ro
# docker compose exec sqlserver bash /tmp/create_schema.sh
- ./sqlserver/create_schema.sh:/tmp/create_schema.sh:ro
dm8:
# wget https://download.dameng.com/eco/dm8/dm8_20230808_rev197096_x86_rh6_64_single.tar
# docker load -i dm8_20230808_rev197096_x86_rh6_64_single.tar
image: dm8_single:dm8_20230808_rev197096_x86_rh6_64
restart: unless-stopped
environment:
PAGE_SIZE: 16
LD_LIBRARY_PATH: /opt/dmdbms/bin
EXTENT_SIZE: 32
BLANK_PAD_MODE: 1
LOG_SIZE: 1024
UNICODE_FLAG: 1
LENGTH_IN_CHAR: 1
INSTANCE_NAME: dm8_test
ports:
- "5236:5236"
volumes:
- dm8:/opt/dmdbms/data
- ../dm/ruoyi-vue-pro-dm8.sql:/tmp/schema.sql:ro
# docker compose exec dm8 bash -c "exec /opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql"