import { Pool, PoolClient, QueryResult, QueryResultRow } from "pg"; import { BasicEntity } from "./entity"; import { DeleteBuilder, IDeleteBuilder, IInsertBuilder, InsertBuilder, ISelectBuilder, IUpdateBuilder, SelectBuilder, UpdateBuilder } from "./query"; import { Class } from "./types"; import { formatSQL } from "./util"; interface IQueryResult extends QueryResult { sql: string; } /** * 数据库基本操作 */ export interface IDatabase { /** * 执行sql语句 * @param sql sql语句 * @param args sql参数 */ query(sql: string, args?: any[] | Record): Promise> /** * 查询实体 * @param Entity 实体 * @param alias 别名 */ select(Entity: Class, alias?: string): ISelectBuilder /** * 进行数据库插入 * @param Entity 实体 */ insert(Entity: Class): IInsertBuilder /** * 进行实体更新 * @param Entity 实体 */ update(Entity: Class): IUpdateBuilder /** * 进行实体删除 * @param Entity 实体 */ delete(Entity: Class): IDeleteBuilder } interface IPostgresClient extends IDatabase { /** 开启事务 */ trans(): Promise; } interface IPostgresDatabase extends IDatabase { /** * 配置数据库 * @param loader 配置加载器 */ config(loader: () => IPostgresConfig): void; /** * 连接数据库 * @param callback 回调 */ connect(callback: (client: IPostgresClient) => R | Promise): Promise /** * 执行事务操作 * @param callback 回调 */ transaction(callback: (client: IPostgresClient) => R | Promise): Promise /** 关闭数据库 */ close(): void; } interface IPostgresConfig { host?: string port?: number user?: string password?: string database?: string max?: number } class PostgresClient implements IPostgresClient { #client: PoolClient #transOn = false; constructor(client: PoolClient) { this.#client = client; } public async query(sql: string, args?: any[] | Record): Promise> { sql = args ? formatSQL(sql, args) : sql; try { const res = await this.#client.query(sql); return { ...res, sql }; } catch (err: any) { err.sql = sql; throw err; } } public async trans() { await this.query("begin") this.#transOn = true; } public get transOn() { return this.#transOn; } public release() { this.#client.release(); } public select(Entity: Class, alias?: string): ISelectBuilder { return new SelectBuilder(this.query.bind(this), Entity, alias); } public insert(Entity: Class): IInsertBuilder { return new InsertBuilder(this.query.bind(this), Entity); } public update(Entity: Class): IUpdateBuilder { return new UpdateBuilder(this.query.bind(this), Entity); } public delete(Entity: Class): IDeleteBuilder { return new DeleteBuilder(this.query.bind(this), Entity); } }; let pool: Pool | null = null; let confLoader!: () => IPostgresConfig function getPool() { if (!pool) { const conf = confLoader(); pool = new Pool({ host: conf.host, port: conf.port, user: conf.user, password: conf.password, database: conf.database, max: conf.max, ssl: false, }); } return pool; } export const database: IPostgresDatabase = { config(loader: () => IPostgresConfig) { confLoader = loader; }, async connect(callback) { const conn = await getPool().connect(); const client = new PostgresClient(conn); let ret: any; try { ret = await callback(client); if (client.transOn) await client.query("commit"); } catch (err) { if (client.transOn) await client.query("rollback"); throw err; } finally { client.release(); } return ret; }, async transaction(callback) { return this.connect(async (client) => { await client.trans(); return callback(client); }); }, close() { if (pool) pool.end().catch((err) => console.error(err)); }, async query(sql, args) { sql = args ? formatSQL(sql, args) : sql; try { const res = await getPool().query(sql); return { ...res, sql }; } catch (err: any) { err.sql = sql; throw err; } }, select(Entity, alias) { return new SelectBuilder(this.query.bind(this), Entity, alias); }, insert(Entity) { return new InsertBuilder(this.query.bind(this), Entity); }, update(Entity) { return new UpdateBuilder(this.query.bind(this), Entity); }, delete(Entity) { return new DeleteBuilder(this.query.bind(this), Entity); }, };