/* * Copyright (c) 2025 SimcuTeam. All rights reserved. * 连接串 → Datasource 工厂:把"驱动与配置构建"收敛到 orm-cj,应用侧只需传连接字符串。 * * 支持两种连接串格式: * 1. URL 格式(推荐):postgres://user:pass@host:port/db?sslmode=disable(opengauss:// 亦可) * 2. ADO.NET 风格:Host=..;Port=..;Database=..;Username=..;Password=..(自动转 URL) * * 驱动通过 std.database.sql.DriverManager 查找:应用只需引入并链接驱动包 * (如 opengauss-driver,其模块加载时自动注册 "postgres"/"opengauss"), * orm-cj 本身不依赖任何具体驱动实现。 */ package simcu::orm.db import std.database.sql.* /** * 连接串 → Datasource 工厂(对齐 .NET NpgsqlDataSource.Create / EF Core UseNpgsql(connStr))。 */ public class DatasourceFactory { /// 默认驱动名(ADO 风格连接串无 scheme 时使用) public static let DEFAULT_DRIVER = "postgres" /** * 由连接字符串创建 Datasource。驱动名自动推断: * URL 格式按 scheme(postgres/postgresql/pgsql/pg → postgres,opengauss → opengauss); * ADO 风格默认 postgres。 */ public static func create(connStr: String): Datasource { build(detectDriver(connStr, DEFAULT_DRIVER), toUrl(connStr)) } /** * 由连接字符串创建 Datasource,可显式指定驱动名(如 "pgsql"/"postgres"/"opengauss")。 * 显式驱动名优先于连接串内 scheme。 */ public static func create(connStr: String, driverName: String): Datasource { build(normalizeDriverName(driverName), toUrl(connStr)) } private static func build(name: String, url: String): Datasource { let driver = DriverManager.getDriver(name) ?? throw Exception( "simorm: 未注册数据库驱动 '${name}'。请确认应用已引入并链接对应驱动包" + "(如 opengauss-driver,其加载时自动注册 postgres/opengauss)") driver.open(url) } /** * 从连接串推断驱动注册名。 */ public static func detectDriver(connStr: String, defaultDriver: String): String { if (connStr.contains("://")) { let i = connStr.indexOf("://").getOrThrow() let scheme = connStr[0..i].toAsciiLower() match (scheme) { case "postgresql" | "pgsql" | "pg" => "postgres" case _ => scheme } } else { normalizeDriverName(defaultDriver) } } /** * 驱动名别名归一化到 DriverManager 注册名。 */ public static func normalizeDriverName(name: String): String { match (name.trimAscii().toAsciiLower()) { case "pgsql" | "pg" | "postgresql" => "postgres" case _ => name.trimAscii() } } /** * 统一为驱动可解析的 URL 格式。 * ADO.NET 风格(.NET Npgsql 连接串)示例: * Host=192.168.0.2;Port=5432;Database=lemon-lurmix;Username=postgres;Password=Love@1314 * → postgres://postgres:Love@1314@192.168.0.2:5432/lemon-lurmix?sslmode=disable * 已是 URL(含 "://")则原样返回。 */ public static func toUrl(connStr: String): String { if (connStr.contains("://")) { return connStr } var host = "" var port = "5432" var database = "" var username = "" var password = "" for (part in connStr.split(";")) { let seg = part.trimAscii() if (seg.isEmpty()) { continue } match (seg.indexOf("=")) { case Some(i) => let key = seg[0..i].trimAscii().toAsciiLower() let value = seg[i + 1..].trimAscii() match (key) { case "host" => host = value case "port" => port = value case "database" => database = value case "username" | "user id" | "uid" => username = value case "password" | "pwd" => password = value case _ => () } case None => () } } return "${DEFAULT_DRIVER}://${username}:${password}@${host}:${port}/${database}?sslmode=disable" } }