diff --git a/package.json b/package.json index aeea625..b21fb3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yizhi/postgres", - "version": "1.0.4", + "version": "1.0.6", "main": "dist/index.js", "types": "typing/index.d.ts", "scripts": {}, diff --git a/src/database.ts b/src/database.ts index ca1d3d6..ce49110 100644 --- a/src/database.ts +++ b/src/database.ts @@ -4,41 +4,66 @@ import { DeleteBuilder, IDeleteBuilder, IInsertBuilder, InsertBuilder, ISelectBu import { Class } from "./types"; import { formatSQL } from "./util"; -interface IPostgresClient { +/** + * 数据库基本操作 + */ +export interface IDatabase { /** * 执行sql语句 * @param sql sql语句 * @param args sql参数 */ - query(sql: string, args?: any[]): Promise>; - - /** 开启事务 */ - trans(): Promise; + query(sql: string, args?: any[] | Record): Promise> /** * 查询实体 * @param Entity 实体 * @param alias 别名 */ - select(Entity: Class, alias?: string): ISelectBuilder; + select(Entity: Class, alias?: string): ISelectBuilder /** * 进行数据库插入 * @param Entity 实体 */ - insert(Entity: Class): IInsertBuilder; + insert(Entity: Class): IInsertBuilder /** * 进行实体更新 * @param Entity 实体 */ - update(Entity: Class): IUpdateBuilder; + update(Entity: Class): IUpdateBuilder /** * 进行实体删除 * @param Entity 实体 */ - del(Entity: Class): IDeleteBuilder; + 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 { @@ -58,7 +83,7 @@ class PostgresClient implements IPostgresClient { this.#client = client; } - public query(sql: string, args?: any[] | Record): Promise> { return this.#client.query(args ? formatSQL(sql, args) : sql); } + public query(sql: string, args?: any[] | Record): Promise> { return this.#client.query(args ? formatSQL(sql, args) : sql); } public async trans() { @@ -69,86 +94,41 @@ class PostgresClient implements IPostgresClient { 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 del(Entity: Class): IDeleteBuilder { return new DeleteBuilder(this.query.bind(this), Entity); } + public delete(Entity: Class): IDeleteBuilder { return new DeleteBuilder(this.query.bind(this), Entity); } }; -/** 数据库操作相关 */ -export namespace database { - 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; +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; +} - /** - * 配置数据库 - * @param loader 配置加载器 - */ - export function config(loader: () => IPostgresConfig) { + +export const database: IPostgresDatabase = { + config(loader: () => IPostgresConfig) { confLoader = loader; - } + }, - /** - * 直接执行sql语句 - * @param sql sql语句 - * @param args sql参数 - */ - export function query(sql: string, args?: any[] | Record) { return getPool().query(args ? formatSQL(sql, args) : sql); } - - /** - * 查询实体 - * @param Entity 实体 - * @param alias 别名 - */ - export function select(Entity: Class, alias?: string): ISelectBuilder { return new SelectBuilder(database.query, Entity, alias); } - - /** - * 进行数据库插入 - * @param Entity 实体 - */ - export function insert(Entity: Class): IInsertBuilder { return new InsertBuilder(database.query, Entity); } - - /** - * 进行实体更新 - * @param Entity 实体 - */ - export function update(Entity: Class): IUpdateBuilder { return new UpdateBuilder(database.query, Entity); } - - /** - * 进行实体删除 - * @param Entity 实体 - */ - export function del(Entity: Class): IDeleteBuilder { return new DeleteBuilder(database.query, Entity); } - - /** - * 连接数据库 - * @param callback 回调 - */ - export async function connect(callback: (client: PostgresClient) => R | Promise): Promise { + async connect(callback) { const conn = await getPool().connect(); const client = new PostgresClient(conn); - let ret: R; + let ret: any; try { ret = await callback(client); if (client.transOn) await client.query("commit"); @@ -162,21 +142,22 @@ export namespace database { } return ret; - } + }, - /** - * 执行事务操作 - * @param callback 回调 - */ - export function transaction(callback: (client: PostgresClient) => R | Promise): Promise { - return connect(async (client) => { + async transaction(callback) { + return this.connect(async (client) => { await client.trans(); return callback(client); }); - }; + }, - /** 关闭数据库 */ - export function close() { + close() { if (pool) pool.end().catch((err) => console.error(err)); - } -} + }, + + query(sql, args) { return getPool().query(args ? formatSQL(sql, args) : sql); }, + 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); }, +}; \ No newline at end of file diff --git a/src/entity.ts b/src/entity.ts index dc778fe..5fbc202 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -2,7 +2,7 @@ import moment from "moment"; import { DatabaseError } from "./error"; import { TSVector } from "./type"; import { escapeID, escapeValue } from "./util"; -import type { ArrayType, ArrayTypeSelf, BasicFieldName, Class, Decorators, IDLikeFieldName, Values } from "./types"; +import type { Class, Decorators, IDLikeFieldName } from "./types"; export interface IEntityFieldConfig { /** 字段名 */ @@ -68,7 +68,9 @@ export class BasicEntity { public pick>(...keys: K[]): Pick { const result: any = {}; for (const k of keys) { - result[k] = this[k]; + let val: any = this[k]; + if (typeof val?.toPickData === "function") val = val.toPickData(); + result[k] = val; } return result; } @@ -82,7 +84,9 @@ export class BasicEntity { const config = getEntityConfigWithoutCheck(this.constructor)!; for (const col of config.fields) { if (keys.includes(col.prop as K)) continue; - result[col.prop] = this[col.prop as K]; + let val: any = this[col.prop as K]; + if (typeof val?.toPickData === "function") val = val.toPickData(); + result[col.prop as K] = val; } return result; } diff --git a/src/index.ts b/src/index.ts index 5c51879..5736247 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,5 @@ +import "./types"; + export { Table, Field, IntColumn, FloatColumn, StringColumn, BooleanColumn, DateColumn, DateTimeColumn, JsonColumn, diff --git a/src/query.ts b/src/query.ts index 12f9ee4..f59ef2e 100644 --- a/src/query.ts +++ b/src/query.ts @@ -1,10 +1,10 @@ -import { Pool, PoolClient, QueryResult, QueryResultRow } from "pg"; +import { QueryResult, QueryResultRow } from "pg"; import { BasicEntity, IEntityFieldConfig, IEntityJoinConfig, getEntityConfig, getField, getJoin, getTableName } from "./entity"; -import type { JoinaFieldName, Class, ArrayTypeSelf, Values } from "./types"; +import type { JoinaFieldName, Class, ArrayTypeSelf, Values, StringArgs, SQLStringArgs } from "./types"; import { escapeID, escapeValue, formatSQL } from "./util"; -//编译器原因,不加此段代码会出错,具体原因未知,请保持定义和entity.ts重的同名定义一致 +//编译器原因,不加此段代码会出错,具体原因未知,请保持定义和types.ts重的同名定义一致 /** 取基础类型的字段名 */ export type BasicFieldName = Values<{ [P in keyof T]: //忽略never @@ -25,7 +25,6 @@ interface IQueryFunc { type DataOrCustom = T | (() => string); -type StringArgs = Str extends `${string}${P}${infer R}${S}${infer T}` ? (R | StringArgs) : never; type BasicWhere = never @@ -64,7 +63,7 @@ interface IWhereFn { */ where(option: ConditionOption): this where(sql: string, args?: any[]): this - where(sql: S, args: { [P in StringArgs]: any }): this + where(sql: S, args: { [P in SQLStringArgs]: any }): this /** * 添加搜索条件 diff --git a/src/types.ts b/src/types.ts index 895d33f..7605801 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,20 +14,22 @@ export namespace Decorators { export type ParamDecorator = (target: T, propertyKey: string, parameterIndex: number) => any; } +declare global { -/** 取基础类型的字段名 */ -export type BasicFieldName = Values<{ [P in keyof T]: - //忽略never - Exclude extends never ? never : ( - //忽略函数 - Exclude extends Function ? never : ( - //忽略实体 - ArrayTypeSelf> extends BasicEntity ? never : ( - P + /** 取基础类型的字段名 */ + type BasicFieldName = Values<{ [P in keyof T]: + //忽略never + Exclude extends never ? never : ( + //忽略函数 + Exclude extends Function ? never : ( + //忽略实体 + ArrayTypeSelf> extends BasicEntity ? never : ( + P + ) ) ) - ) -}>; + }>; +} /** 可以用作ID的字段名称 */ @@ -59,4 +61,20 @@ export type OneJoinFieldName = Values<{ [P in keyof T]: }>; /** 可以Join的字段名 */ -export type JoinaFieldName = OneJoinFieldName | ManyJoinFieldName; \ No newline at end of file +export type JoinaFieldName = OneJoinFieldName | ManyJoinFieldName; + +/** + * SQL字符串参数 + * + * @param Str SQL字符串 + * @param P 前分割 + * @param S 后分割 + */ +export type StringArgs = Str extends `${string}${P}${infer R}${S}${infer T}` ? (R | StringArgs) : never; + +/** + * SQL字符串参数 + * + * @param Str SQL字符串 + */ +export type SQLStringArgs = StringArgs;