Selaa lähdekoodia

!964 feat: 达梦DM8
Merge pull request !964 from dhb52/feature/db

芋道源码 2 vuotta sitten
vanhempi
commit
a70463b961

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 4251 - 5697
sql/dm/ruoyi-vue-pro-dm8.sql


+ 11 - 2
sql/postgresql/ruoyi-vue-pro.sql

@@ -5,10 +5,19 @@
 
  Target Server Type    : PostgreSQL
 
- Date: 2024-05-01 23:25:45
+ Date: 2024-05-03 23:36:19
 */
 
 
+-- ----------------------------
+-- Table structure for dual
+-- ----------------------------
+DROP TABLE IF EXISTS dual;
+CREATE TABLE dual
+(
+);
+
+
 -- ----------------------------
 -- Table structure for infra_api_access_log
 -- ----------------------------
@@ -3866,7 +3875,6 @@ COMMENT ON COLUMN system_sms_code.updater IS '更新者';
 COMMENT ON COLUMN system_sms_code.update_time IS '更新时间';
 COMMENT ON COLUMN system_sms_code.deleted IS '是否删除';
 COMMENT ON COLUMN system_sms_code.tenant_id IS '租户编号';
-COMMENT ON COLUMN system_sms_code.idx_mobile IS '手机号';
 COMMENT ON TABLE system_sms_code IS '手机验证码';
 
 DROP SEQUENCE IF EXISTS system_sms_code_seq;
@@ -4703,3 +4711,4 @@ COMMIT;
 DROP SEQUENCE IF EXISTS yudao_demo03_student_seq;
 CREATE SEQUENCE yudao_demo03_student_seq
     START 10;
+

+ 20 - 1
sql/sqlserver/ruoyi-vue-pro.sql

@@ -5,10 +5,29 @@
 
  Target Server Type    : Microsoft SQL Server
 
- Date: 2024-05-02 15:29:31
+ Date: 2024-05-03 23:36:38
 */
 
 
+-- ----------------------------
+-- Table structure for dual
+-- ----------------------------
+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
+
+
 -- ----------------------------
 -- Table structure for infra_api_access_log
 -- ----------------------------

+ 29 - 2
sql/tools/README.md

@@ -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 实现原理
 

+ 149 - 2
sql/tools/convertor.py

@@ -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}")
 

+ 23 - 0
sql/tools/docker-compose.yaml

@@ -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"

+ 6 - 6
yudao-server/src/main/resources/application-local.yaml

@@ -50,14 +50,14 @@ spring:
 #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例
 #          url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true # PostgreSQL 连接的示例
 #          url: jdbc:oracle:thin:@//127.0.0.1:1521/XEPDB1 # Oracle 连接的示例
-          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例
-#          url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例
+#          url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例
+          url: jdbc:dm://localhost:5236 # DM 连接的示例
 #          username: root
 #          password: 123456
-          username: sa       # SQLServer 连接的示例
-          password: Yudao@2024 # SQLServer 连接的示例
-#          username: SYSDBA # DM 连接的示例
-#          password: SYSDBA # DM 连接的示例
+#          username: sa       # SQLServer 连接的示例
+#          password: Yudao@2024 # SQLServer 连接的示例
+          username: SYSDBA # DM 连接的示例
+          password: SYSDBA001 # DM 连接的示例
         slave: # 模拟从库,可根据自己需要修改
           lazy: true # 开启懒加载,保证启动速度
 #          url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例