@zhin.js/database 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1360 -34
  3. package/lib/base/database.d.ts +71 -13
  4. package/lib/base/database.d.ts.map +1 -1
  5. package/lib/base/database.js +128 -4
  6. package/lib/base/database.js.map +1 -1
  7. package/lib/base/dialect.d.ts +27 -10
  8. package/lib/base/dialect.d.ts.map +1 -1
  9. package/lib/base/dialect.js +32 -0
  10. package/lib/base/dialect.js.map +1 -1
  11. package/lib/base/index.d.ts +1 -0
  12. package/lib/base/index.d.ts.map +1 -1
  13. package/lib/base/index.js +1 -0
  14. package/lib/base/index.js.map +1 -1
  15. package/lib/base/model.d.ts +105 -12
  16. package/lib/base/model.d.ts.map +1 -1
  17. package/lib/base/model.js +224 -3
  18. package/lib/base/model.js.map +1 -1
  19. package/lib/base/query-classes.d.ts +204 -33
  20. package/lib/base/query-classes.d.ts.map +1 -1
  21. package/lib/base/query-classes.js +276 -0
  22. package/lib/base/query-classes.js.map +1 -1
  23. package/lib/base/thenable.d.ts +7 -7
  24. package/lib/base/thenable.d.ts.map +1 -1
  25. package/lib/base/thenable.js +5 -4
  26. package/lib/base/thenable.js.map +1 -1
  27. package/lib/base/transaction.d.ts +46 -0
  28. package/lib/base/transaction.d.ts.map +1 -0
  29. package/lib/base/transaction.js +186 -0
  30. package/lib/base/transaction.js.map +1 -0
  31. package/lib/dialects/memory.d.ts +13 -8
  32. package/lib/dialects/memory.d.ts.map +1 -1
  33. package/lib/dialects/memory.js +10 -7
  34. package/lib/dialects/memory.js.map +1 -1
  35. package/lib/dialects/mongodb.d.ts +12 -8
  36. package/lib/dialects/mongodb.d.ts.map +1 -1
  37. package/lib/dialects/mongodb.js +21 -18
  38. package/lib/dialects/mongodb.js.map +1 -1
  39. package/lib/dialects/mysql.d.ts +36 -7
  40. package/lib/dialects/mysql.d.ts.map +1 -1
  41. package/lib/dialects/mysql.js +140 -21
  42. package/lib/dialects/mysql.js.map +1 -1
  43. package/lib/dialects/pg.d.ts +36 -7
  44. package/lib/dialects/pg.d.ts.map +1 -1
  45. package/lib/dialects/pg.js +140 -21
  46. package/lib/dialects/pg.js.map +1 -1
  47. package/lib/dialects/redis.d.ts +13 -8
  48. package/lib/dialects/redis.d.ts.map +1 -1
  49. package/lib/dialects/redis.js +14 -11
  50. package/lib/dialects/redis.js.map +1 -1
  51. package/lib/dialects/sqlite.d.ts +20 -7
  52. package/lib/dialects/sqlite.d.ts.map +1 -1
  53. package/lib/dialects/sqlite.js +66 -13
  54. package/lib/dialects/sqlite.js.map +1 -1
  55. package/lib/index.d.ts +1 -0
  56. package/lib/index.d.ts.map +1 -1
  57. package/lib/index.js +1 -0
  58. package/lib/index.js.map +1 -1
  59. package/lib/migration.d.ts +132 -0
  60. package/lib/migration.d.ts.map +1 -0
  61. package/lib/migration.js +475 -0
  62. package/lib/migration.js.map +1 -0
  63. package/lib/registry.d.ts +26 -23
  64. package/lib/registry.d.ts.map +1 -1
  65. package/lib/registry.js +1 -5
  66. package/lib/registry.js.map +1 -1
  67. package/lib/type/document/database.d.ts +12 -12
  68. package/lib/type/document/database.d.ts.map +1 -1
  69. package/lib/type/document/database.js +1 -1
  70. package/lib/type/document/database.js.map +1 -1
  71. package/lib/type/document/model.d.ts +8 -8
  72. package/lib/type/document/model.d.ts.map +1 -1
  73. package/lib/type/document/model.js +1 -1
  74. package/lib/type/document/model.js.map +1 -1
  75. package/lib/type/keyvalue/database.d.ts +12 -12
  76. package/lib/type/keyvalue/database.d.ts.map +1 -1
  77. package/lib/type/keyvalue/database.js +1 -1
  78. package/lib/type/keyvalue/database.js.map +1 -1
  79. package/lib/type/keyvalue/model.d.ts +3 -3
  80. package/lib/type/keyvalue/model.d.ts.map +1 -1
  81. package/lib/type/keyvalue/model.js +1 -1
  82. package/lib/type/keyvalue/model.js.map +1 -1
  83. package/lib/type/related/database.d.ts +49 -14
  84. package/lib/type/related/database.d.ts.map +1 -1
  85. package/lib/type/related/database.js +259 -28
  86. package/lib/type/related/database.js.map +1 -1
  87. package/lib/type/related/model.d.ts +252 -16
  88. package/lib/type/related/model.d.ts.map +1 -1
  89. package/lib/type/related/model.js +648 -23
  90. package/lib/type/related/model.js.map +1 -1
  91. package/lib/types.d.ts +475 -37
  92. package/lib/types.d.ts.map +1 -1
  93. package/lib/types.js +6 -0
  94. package/lib/types.js.map +1 -1
  95. package/package.json +10 -5
  96. package/src/base/database.ts +168 -24
  97. package/src/base/dialect.ts +49 -10
  98. package/src/base/index.ts +2 -1
  99. package/src/base/model.ts +258 -18
  100. package/src/base/query-classes.ts +471 -63
  101. package/src/base/thenable.ts +12 -11
  102. package/src/base/transaction.ts +213 -0
  103. package/src/dialects/memory.ts +17 -16
  104. package/src/dialects/mongodb.ts +44 -42
  105. package/src/dialects/mysql.ts +155 -26
  106. package/src/dialects/pg.ts +152 -25
  107. package/src/dialects/redis.ts +45 -43
  108. package/src/dialects/sqlite.ts +77 -19
  109. package/src/index.ts +1 -2
  110. package/src/migration.ts +544 -0
  111. package/src/registry.ts +33 -33
  112. package/src/type/document/database.ts +33 -33
  113. package/src/type/document/model.ts +15 -15
  114. package/src/type/keyvalue/database.ts +33 -33
  115. package/src/type/keyvalue/model.ts +19 -19
  116. package/src/type/related/database.ts +309 -34
  117. package/src/type/related/model.ts +801 -34
  118. package/src/types.ts +559 -44
  119. package/tests/database.test.ts +1738 -0
@@ -1,28 +1,50 @@
1
- import {Dialect} from '../base';
2
- import {RelatedDatabase} from "../type/related/database";
3
- import {Registry} from "../registry";
4
- import type { ConnectionOptions } from 'mysql2/promise';
5
- import {Database} from "../base";
6
- import {Column} from "../types";
1
+ import {Dialect} from '../base/index.js';
2
+ import {RelatedDatabase} from "../type/related/database.js";
3
+ import {Registry} from "../registry.js";
4
+ import type { ConnectionOptions, PoolOptions } from 'mysql2/promise';
5
+ import {Database} from "../base/index.js";
6
+ import {Column, Transaction, TransactionOptions, IsolationLevel, PoolConfig} from "../types.js";
7
7
 
8
- export interface MySQLDialectConfig extends ConnectionOptions {}
8
+ export interface MySQLDialectConfig extends ConnectionOptions {
9
+ /**
10
+ * 连接池配置
11
+ * 如果提供此选项,将使用连接池而不是单连接
12
+ */
13
+ pool?: PoolConfig;
14
+ }
9
15
 
10
- export class MySQLDialect extends Dialect<MySQLDialectConfig, string> {
16
+ export class MySQLDialect<S extends Record<string, object> = Record<string, object>> extends Dialect<MySQLDialectConfig, S, string> {
11
17
  private connection: any = null;
18
+ private pool: any = null;
19
+ private usePool: boolean = false;
12
20
 
13
21
  constructor(config: MySQLDialectConfig) {
14
22
  super('mysql', config);
23
+ this.usePool = !!config.pool;
15
24
  }
16
25
 
17
26
  // Connection management
18
27
  isConnected(): boolean {
19
- return this.connection !== null;
28
+ return this.usePool ? this.pool !== null : this.connection !== null;
20
29
  }
21
30
 
22
31
  async connect(): Promise<void> {
23
32
  try {
33
+ if (this.usePool) {
34
+ const { createPool } = await import('mysql2/promise');
35
+ const poolConfig: PoolOptions = {
36
+ ...this.config,
37
+ waitForConnections: true,
38
+ connectionLimit: this.config.pool?.max ?? 10,
39
+ queueLimit: 0,
40
+ idleTimeout: this.config.pool?.idleTimeoutMillis ?? 60000,
41
+ };
42
+ this.pool = createPool(poolConfig);
43
+ console.log(`MySQL 连接池已创建 (max: ${poolConfig.connectionLimit})`);
44
+ } else {
24
45
  const { createConnection } = await import('mysql2/promise');
25
46
  this.connection = await createConnection(this.config);
47
+ }
26
48
  } catch (error) {
27
49
  console.error('forgot install mysql2 ?');
28
50
  throw new Error(`MySQL 连接失败: ${error}`);
@@ -30,23 +52,50 @@ export class MySQLDialect extends Dialect<MySQLDialectConfig, string> {
30
52
  }
31
53
 
32
54
  async disconnect(): Promise<void> {
55
+ if (this.usePool && this.pool) {
56
+ await this.pool.end();
57
+ this.pool = null;
58
+ console.log('MySQL 连接池已关闭');
59
+ } else if (this.connection) {
60
+ await this.connection.end();
33
61
  this.connection = null;
62
+ }
34
63
  }
35
64
 
36
65
  async healthCheck(): Promise<boolean> {
37
- return this.isConnected();
66
+ if (!this.isConnected()) return false;
67
+ try {
68
+ await this.query('SELECT 1');
69
+ return true;
70
+ } catch {
71
+ return false;
72
+ }
38
73
  }
39
74
 
40
75
  async query<U = any>(sql: string, params?: any[]): Promise<U> {
76
+ if (this.usePool) {
77
+ const [rows] = await this.pool.execute(sql, params);
78
+ return rows as U;
79
+ } else {
41
80
  const [rows] = await this.connection.execute(sql, params);
42
81
  return rows as U;
82
+ }
43
83
  }
44
84
 
45
85
  async dispose(): Promise<void> {
46
- if (this.connection) {
47
- await this.connection.end();
48
- this.connection = null;
49
- }
86
+ await this.disconnect();
87
+ }
88
+
89
+ /**
90
+ * 获取连接池统计信息(仅在使用连接池时有效)
91
+ */
92
+ getPoolStats(): { total: number; idle: number; waiting: number } | null {
93
+ if (!this.usePool || !this.pool) return null;
94
+ return {
95
+ total: this.pool.pool?._allConnections?.length ?? 0,
96
+ idle: this.pool.pool?._freeConnections?.length ?? 0,
97
+ waiting: this.pool.pool?._connectionQueue?.length ?? 0,
98
+ };
50
99
  }
51
100
 
52
101
  // SQL generation methods
@@ -118,8 +167,8 @@ export class MySQLDialect extends Dialect<MySQLDialectConfig, string> {
118
167
  return `LIMIT ${offset}, ${limit}`;
119
168
  }
120
169
 
121
- formatCreateTable(tableName: string, columns: string[]): string {
122
- return `CREATE TABLE IF NOT EXISTS ${this.quoteIdentifier(tableName)} (${columns.join(', ')}) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`;
170
+ formatCreateTable<T extends keyof S>(tableName: T, columns: string[]): string {
171
+ return `CREATE TABLE IF NOT EXISTS ${this.quoteIdentifier(String(tableName))} (${columns.join(', ')}) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`;
123
172
  }
124
173
 
125
174
  formatColumnDefinition(field: string, column: Column<any>): string {
@@ -137,21 +186,101 @@ export class MySQLDialect extends Dialect<MySQLDialectConfig, string> {
137
186
  return `${name} ${type}${length}${primary}${unique}${autoIncrement}${nullable}${defaultVal}`;
138
187
  }
139
188
 
140
- formatAlterTable(tableName: string, alterations: string[]): string {
141
- return `ALTER TABLE ${this.quoteIdentifier(tableName)} ${alterations.join(', ')}`;
189
+ formatAlterTable<T extends keyof S>(tableName: T, alterations: string[]): string {
190
+ return `ALTER TABLE ${this.quoteIdentifier(String(tableName))} ${alterations.join(', ')}`;
142
191
  }
143
192
 
144
- formatDropTable(tableName: string, ifExists?: boolean): string {
193
+ formatDropTable<T extends keyof S>(tableName: T, ifExists?: boolean): string {
145
194
  const ifExistsClause = ifExists ? 'IF EXISTS ' : '';
146
- return `DROP TABLE ${ifExistsClause}${this.quoteIdentifier(tableName)}`;
195
+ return `DROP TABLE ${ifExistsClause}${this.quoteIdentifier(String(tableName))}`;
147
196
  }
148
197
 
149
- formatDropIndex(indexName: string, tableName: string, ifExists?: boolean): string {
198
+ formatDropIndex<T extends keyof S>(indexName: string, tableName: T, ifExists?: boolean): string {
150
199
  const ifExistsClause = ifExists ? 'IF EXISTS ' : '';
151
- return `DROP INDEX ${ifExistsClause}${this.quoteIdentifier(indexName)} ON ${this.quoteIdentifier(tableName)}`;
200
+ return `DROP INDEX ${ifExistsClause}${this.quoteIdentifier(indexName)} ON ${this.quoteIdentifier(String(tableName))}`;
201
+ }
202
+
203
+ // ============================================================================
204
+ // Transaction Support
205
+ // ============================================================================
206
+
207
+ /**
208
+ * MySQL 支持事务
209
+ */
210
+ supportsTransactions(): boolean {
211
+ return true;
212
+ }
213
+
214
+ /**
215
+ * 开始事务
216
+ * 在连接池模式下,会获取一个专用连接用于事务
217
+ */
218
+ async beginTransaction(options?: TransactionOptions): Promise<Transaction> {
219
+ if (this.usePool) {
220
+ // 从连接池获取一个连接用于事务
221
+ const connection = await this.pool.getConnection();
222
+
223
+ // 设置隔离级别
224
+ if (options?.isolationLevel) {
225
+ await connection.execute(`SET TRANSACTION ISOLATION LEVEL ${this.formatIsolationLevel(options.isolationLevel)}`);
226
+ }
227
+
228
+ // 开始事务
229
+ await connection.execute('START TRANSACTION');
230
+
231
+ return {
232
+ async commit(): Promise<void> {
233
+ try {
234
+ await connection.execute('COMMIT');
235
+ } finally {
236
+ connection.release(); // 归还连接到池
237
+ }
238
+ },
239
+
240
+ async rollback(): Promise<void> {
241
+ try {
242
+ await connection.execute('ROLLBACK');
243
+ } finally {
244
+ connection.release(); // 归还连接到池
245
+ }
246
+ },
247
+
248
+ async query<T = any>(sql: string, params?: any[]): Promise<T> {
249
+ const [rows] = await connection.execute(sql, params);
250
+ return rows as T;
251
+ }
252
+ };
253
+ } else {
254
+ // 单连接模式
255
+ const dialect = this;
256
+
257
+ // 设置隔离级别
258
+ if (options?.isolationLevel) {
259
+ await this.query(`SET TRANSACTION ISOLATION LEVEL ${this.formatIsolationLevel(options.isolationLevel)}`);
260
+ }
261
+
262
+ // 开始事务
263
+ await this.query('START TRANSACTION');
264
+
265
+ return {
266
+ async commit(): Promise<void> {
267
+ await dialect.query('COMMIT');
268
+ },
269
+
270
+ async rollback(): Promise<void> {
271
+ await dialect.query('ROLLBACK');
272
+ },
273
+
274
+ async query<T = any>(sql: string, params?: any[]): Promise<T> {
275
+ return dialect.query<T>(sql, params);
276
+ }
277
+ };
278
+ }
152
279
  }
153
280
  }
154
-
155
- Registry.register('mysql', (config: MySQLDialectConfig, definitions?: Database.Definitions<Record<string, object>>) => {
156
- return new RelatedDatabase(new MySQLDialect(config), definitions);
157
- });
281
+ export class MySQL<S extends Record<string, object> = Record<string, object>> extends RelatedDatabase<MySQLDialectConfig, S> {
282
+ constructor(config: MySQLDialectConfig, definitions?: Database.DefinitionObj<S>) {
283
+ super(new MySQLDialect<S>(config), definitions);
284
+ }
285
+ }
286
+ Registry.register('mysql', MySQL);
@@ -1,29 +1,51 @@
1
- import {Dialect} from '../base';
2
- import {RelatedDatabase} from "../type/related/database";
3
- import {Database} from "../base";
4
- import {Registry} from "../registry";
5
- import type { ClientConfig } from 'pg';
6
- import {Column} from "../types";
1
+ import {Dialect} from '../base/index.js';
2
+ import {RelatedDatabase} from "../type/related/database.js";
3
+ import {Database} from "../base/index.js";
4
+ import {Registry} from "../registry.js";
5
+ import type { ClientConfig, PoolConfig as PgPoolConfig } from 'pg';
6
+ import {Column, Transaction, TransactionOptions, PoolConfig} from "../types.js";
7
7
 
8
- export interface PostgreSQLDialectConfig extends ClientConfig {}
8
+ export interface PostgreSQLDialectConfig extends ClientConfig {
9
+ /**
10
+ * 连接池配置
11
+ * 如果提供此选项,将使用连接池而不是单连接
12
+ */
13
+ pool?: PoolConfig;
14
+ }
9
15
 
10
- export class PostgreSQLDialect extends Dialect<PostgreSQLDialectConfig, string> {
16
+ export class PostgreSQLDialect<S extends Record<string, object> = Record<string, object>> extends Dialect<PostgreSQLDialectConfig, S, string> {
11
17
  private connection: any = null;
18
+ private pool: any = null;
19
+ private usePool: boolean = false;
12
20
 
13
21
  constructor(config: PostgreSQLDialectConfig) {
14
22
  super('pg', config);
23
+ this.usePool = !!config.pool;
15
24
  }
16
25
 
17
26
  // Connection management
18
27
  isConnected(): boolean {
19
- return this.connection !== null;
28
+ return this.usePool ? this.pool !== null : this.connection !== null;
20
29
  }
21
30
 
22
31
  async connect(): Promise<void> {
23
32
  try {
33
+ if (this.usePool) {
34
+ const { Pool } = await import('pg');
35
+ const poolConfig: PgPoolConfig = {
36
+ ...this.config,
37
+ max: this.config.pool?.max ?? 10,
38
+ min: this.config.pool?.min ?? 2,
39
+ idleTimeoutMillis: this.config.pool?.idleTimeoutMillis ?? 30000,
40
+ connectionTimeoutMillis: this.config.pool?.acquireTimeoutMillis ?? 10000,
41
+ };
42
+ this.pool = new Pool(poolConfig);
43
+ console.log(`PostgreSQL 连接池已创建 (max: ${poolConfig.max})`);
44
+ } else {
24
45
  const { Client } = await import('pg');
25
46
  this.connection = new Client(this.config);
26
47
  await this.connection.connect();
48
+ }
27
49
  } catch (error) {
28
50
  console.error('forgot install pg ?');
29
51
  throw new Error(`PostgreSQL 连接失败: ${error}`);
@@ -31,23 +53,50 @@ export class PostgreSQLDialect extends Dialect<PostgreSQLDialectConfig, string>
31
53
  }
32
54
 
33
55
  async disconnect(): Promise<void> {
56
+ if (this.usePool && this.pool) {
57
+ await this.pool.end();
58
+ this.pool = null;
59
+ console.log('PostgreSQL 连接池已关闭');
60
+ } else if (this.connection) {
61
+ await this.connection.end();
34
62
  this.connection = null;
63
+ }
35
64
  }
36
65
 
37
66
  async healthCheck(): Promise<boolean> {
38
- return this.isConnected();
67
+ if (!this.isConnected()) return false;
68
+ try {
69
+ await this.query('SELECT 1');
70
+ return true;
71
+ } catch {
72
+ return false;
73
+ }
39
74
  }
40
75
 
41
76
  async query<U = any>(sql: string, params?: any[]): Promise<U> {
77
+ if (this.usePool) {
78
+ const result = await this.pool.query(sql, params);
79
+ return result.rows as U;
80
+ } else {
42
81
  const result = await this.connection.query(sql, params);
43
82
  return result.rows as U;
83
+ }
44
84
  }
45
85
 
46
86
  async dispose(): Promise<void> {
47
- if (this.connection) {
48
- await this.connection.end();
49
- this.connection = null;
50
- }
87
+ await this.disconnect();
88
+ }
89
+
90
+ /**
91
+ * 获取连接池统计信息(仅在使用连接池时有效)
92
+ */
93
+ getPoolStats(): { total: number; idle: number; waiting: number } | null {
94
+ if (!this.usePool || !this.pool) return null;
95
+ return {
96
+ total: this.pool.totalCount ?? 0,
97
+ idle: this.pool.idleCount ?? 0,
98
+ waiting: this.pool.waitingCount ?? 0,
99
+ };
51
100
  }
52
101
 
53
102
  // SQL generation methods
@@ -119,8 +168,8 @@ export class PostgreSQLDialect extends Dialect<PostgreSQLDialectConfig, string>
119
168
  return `LIMIT ${limit} OFFSET ${offset}`;
120
169
  }
121
170
 
122
- formatCreateTable(tableName: string, columns: string[]): string {
123
- return `CREATE TABLE IF NOT EXISTS ${this.quoteIdentifier(tableName)} (${columns.join(', ')})`;
171
+ formatCreateTable<T extends keyof S>(tableName: T, columns: string[]): string {
172
+ return `CREATE TABLE IF NOT EXISTS ${this.quoteIdentifier(String(tableName))} (${columns.join(', ')})`;
124
173
  }
125
174
 
126
175
  formatColumnDefinition(field: string, column: Column<any>): string {
@@ -137,21 +186,99 @@ export class PostgreSQLDialect extends Dialect<PostgreSQLDialectConfig, string>
137
186
  return `${name} ${type}${length}${primary}${unique}${nullable}${defaultVal}`;
138
187
  }
139
188
 
140
- formatAlterTable(tableName: string, alterations: string[]): string {
141
- return `ALTER TABLE ${this.quoteIdentifier(tableName)} ${alterations.join(', ')}`;
189
+ formatAlterTable<T extends keyof S>(tableName: T, alterations: string[]): string {
190
+ return `ALTER TABLE ${this.quoteIdentifier(String(tableName))} ${alterations.join(', ')}`;
142
191
  }
143
192
 
144
- formatDropTable(tableName: string, ifExists?: boolean): string {
193
+ formatDropTable<T extends keyof S>(tableName: T, ifExists?: boolean): string {
145
194
  const ifExistsClause = ifExists ? 'IF EXISTS ' : '';
146
- return `DROP TABLE ${ifExistsClause}${this.quoteIdentifier(tableName)}`;
195
+ return `DROP TABLE ${ifExistsClause}${this.quoteIdentifier(String(tableName))}`;
147
196
  }
148
197
 
149
- formatDropIndex(indexName: string, tableName: string, ifExists?: boolean): string {
198
+ formatDropIndex<T extends keyof S>(indexName: string, tableName: T, ifExists?: boolean): string {
150
199
  const ifExistsClause = ifExists ? 'IF EXISTS ' : '';
151
200
  return `DROP INDEX ${ifExistsClause}${this.quoteIdentifier(indexName)}`;
152
201
  }
202
+
203
+ // ============================================================================
204
+ // Transaction Support
205
+ // ============================================================================
206
+
207
+ /**
208
+ * PostgreSQL 支持事务
209
+ */
210
+ supportsTransactions(): boolean {
211
+ return true;
212
+ }
213
+
214
+ /**
215
+ * 开始事务
216
+ * 在连接池模式下,会获取一个专用连接用于事务
217
+ */
218
+ async beginTransaction(options?: TransactionOptions): Promise<Transaction> {
219
+ if (this.usePool) {
220
+ // 从连接池获取一个连接用于事务
221
+ const client = await this.pool.connect();
222
+
223
+ // 开始事务(可以带隔离级别)
224
+ let beginSql = 'BEGIN';
225
+ if (options?.isolationLevel) {
226
+ beginSql = `BEGIN ISOLATION LEVEL ${this.formatIsolationLevel(options.isolationLevel)}`;
227
+ }
228
+ await client.query(beginSql);
229
+
230
+ return {
231
+ async commit(): Promise<void> {
232
+ try {
233
+ await client.query('COMMIT');
234
+ } finally {
235
+ client.release(); // 归还连接到池
236
+ }
237
+ },
238
+
239
+ async rollback(): Promise<void> {
240
+ try {
241
+ await client.query('ROLLBACK');
242
+ } finally {
243
+ client.release(); // 归还连接到池
244
+ }
245
+ },
246
+
247
+ async query<T = any>(sql: string, params?: any[]): Promise<T> {
248
+ const result = await client.query(sql, params);
249
+ return result.rows as T;
250
+ }
251
+ };
252
+ } else {
253
+ // 单连接模式
254
+ const dialect = this;
255
+
256
+ // 开始事务(可以带隔离级别)
257
+ let beginSql = 'BEGIN';
258
+ if (options?.isolationLevel) {
259
+ beginSql = `BEGIN ISOLATION LEVEL ${this.formatIsolationLevel(options.isolationLevel)}`;
260
+ }
261
+ await this.query(beginSql);
262
+
263
+ return {
264
+ async commit(): Promise<void> {
265
+ await dialect.query('COMMIT');
266
+ },
267
+
268
+ async rollback(): Promise<void> {
269
+ await dialect.query('ROLLBACK');
270
+ },
271
+
272
+ async query<T = any>(sql: string, params?: any[]): Promise<T> {
273
+ return dialect.query<T>(sql, params);
274
+ }
275
+ };
276
+ }
277
+ }
153
278
  }
154
-
155
- Registry.register('pg', (config: PostgreSQLDialectConfig, definitions?: Database.Definitions<Record<string, object>>) => {
156
- return new RelatedDatabase(new PostgreSQLDialect(config), definitions);
157
- });
279
+ export class PG<S extends Record<string, object> = Record<string, object>> extends RelatedDatabase<PostgreSQLDialectConfig, S> {
280
+ constructor(config: PostgreSQLDialectConfig, definitions?: Database.DefinitionObj<S>) {
281
+ super(new PostgreSQLDialect<S>(config), definitions);
282
+ }
283
+ }
284
+ Registry.register('pg', PG);
@@ -1,7 +1,7 @@
1
- import {Dialect} from "../base";
2
- import {KeyValueDatabase} from "../type/keyvalue/database";
3
- import {Registry} from "../registry";
4
- import {Database} from "../base";
1
+ import {Dialect} from "../base/index.js";
2
+ import {KeyValueDatabase} from "../type/keyvalue/database.js";
3
+ import {Registry} from "../registry.js";
4
+ import {Database} from "../base/index.js";
5
5
  import type { RedisClientOptions } from 'redis';
6
6
  import {
7
7
  BuildQueryResult,
@@ -16,10 +16,10 @@ import {
16
16
  AlterQueryParams,
17
17
  DropTableQueryParams,
18
18
  DropIndexQueryParams
19
- } from "../types";
19
+ } from "../types.js";
20
20
 
21
21
  export interface RedisDialectConfig extends RedisClientOptions {}
22
- export class RedisDialect extends Dialect<RedisDialectConfig, KeyValueQueryResult> {
22
+ export class RedisDialect<S extends Record<string, object> = Record<string, object>> extends Dialect<RedisDialectConfig, S, KeyValueQueryResult> {
23
23
  private client: any = null;
24
24
 
25
25
  constructor(config: RedisDialectConfig) {
@@ -164,24 +164,24 @@ export class RedisDialect extends Dialect<RedisDialectConfig, KeyValueQueryResul
164
164
  /**
165
165
  * 构建查询
166
166
  */
167
- buildQuery<U extends object = any>(params: QueryParams<U>): BuildQueryResult<KeyValueQueryResult> {
167
+ buildQuery<T extends keyof S>(params: QueryParams<S, T>): BuildQueryResult<KeyValueQueryResult> {
168
168
  switch (params.type) {
169
169
  case 'create':
170
- return this.buildCreateQuery(params as CreateQueryParams<U>);
170
+ return this.buildCreateQuery(params as CreateQueryParams<S, T>);
171
171
  case 'select':
172
- return this.buildSelectQuery(params as SelectQueryParams<U>);
172
+ return this.buildSelectQuery(params as SelectQueryParams<S, T>);
173
173
  case 'insert':
174
- return this.buildInsertQuery(params as InsertQueryParams<U>);
174
+ return this.buildInsertQuery(params as InsertQueryParams<S, T>);
175
175
  case 'update':
176
- return this.buildUpdateQuery(params as UpdateQueryParams<U>);
176
+ return this.buildUpdateQuery(params as UpdateQueryParams<S, T>);
177
177
  case 'delete':
178
- return this.buildDeleteQuery(params as DeleteQueryParams<U>);
178
+ return this.buildDeleteQuery(params as DeleteQueryParams<S, T>);
179
179
  case 'alter':
180
- return this.buildAlterQuery(params as AlterQueryParams<U>);
180
+ return this.buildAlterQuery(params as AlterQueryParams<S, T>);
181
181
  case 'drop_table':
182
- return this.buildDropTableQuery(params as DropTableQueryParams<U>);
182
+ return this.buildDropTableQuery(params as DropTableQueryParams<S, T>);
183
183
  case 'drop_index':
184
- return this.buildDropIndexQuery(params as DropIndexQueryParams);
184
+ return this.buildDropIndexQuery(params as DropIndexQueryParams<S, T>);
185
185
  default:
186
186
  throw new Error(`不支持的查询类型: ${(params as any).type}`);
187
187
  }
@@ -236,36 +236,36 @@ export class RedisDialect extends Dialect<RedisDialectConfig, KeyValueQueryResul
236
236
  return `${limit},${offset}`;
237
237
  }
238
238
 
239
- formatCreateTable(tableName: string, columns: string[]): string {
240
- return `CREATE TABLE ${tableName} (${columns.join(',')})`;
239
+ formatCreateTable(tableName: keyof S, columns: string[]): string {
240
+ return `CREATE TABLE ${String(tableName)} (${columns.join(',')})`;
241
241
  }
242
242
 
243
- formatAlterTable(tableName: string, alterations: string[]): string {
244
- return `ALTER TABLE ${tableName} ${alterations.join(',')}`;
243
+ formatAlterTable(tableName: keyof S, alterations: string[]): string {
244
+ return `ALTER TABLE ${String(tableName)} ${alterations.join(',')}`;
245
245
  }
246
246
 
247
- formatDropTable(tableName: string, ifExists?: boolean): string {
248
- return `DROP TABLE ${tableName} ${ifExists ? 'IF EXISTS' : ''}`;
247
+ formatDropTable(tableName: keyof S, ifExists?: boolean): string {
248
+ return `DROP TABLE ${String(tableName)} ${ifExists ? 'IF EXISTS' : ''}`;
249
249
  }
250
250
 
251
- formatDropIndex(indexName: string, tableName: string, ifExists?: boolean): string {
252
- return `DROP INDEX ${indexName} ON ${tableName} ${ifExists ? 'IF EXISTS' : ''}`;
251
+ formatDropIndex(indexName: string, tableName: keyof S, ifExists?: boolean): string {
252
+ return `DROP INDEX ${indexName} ON ${String(tableName)} ${ifExists ? 'IF EXISTS' : ''}`;
253
253
  }
254
254
 
255
255
  // Redis 特定的查询构建方法
256
- private buildCreateQuery<T extends object>(params: CreateQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
256
+ private buildCreateQuery<T extends keyof S>(params: CreateQueryParams<S, T>): BuildQueryResult<KeyValueQueryResult> {
257
257
  return {
258
258
  query: {
259
- bucket: params.tableName,
259
+ bucket: String(params.tableName),
260
260
  operation: 'keys'
261
261
  },
262
262
  params: []
263
263
  };
264
264
  }
265
265
 
266
- private buildSelectQuery<T extends object>(params: SelectQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
266
+ private buildSelectQuery<T extends keyof S>(params: SelectQueryParams<S, T>): BuildQueryResult<KeyValueQueryResult> {
267
267
  const query: KeyValueQueryResult = {
268
- bucket: params.tableName,
268
+ bucket: params.tableName as string,
269
269
  operation: 'keys'
270
270
  };
271
271
 
@@ -284,12 +284,12 @@ export class RedisDialect extends Dialect<RedisDialectConfig, KeyValueQueryResul
284
284
  };
285
285
  }
286
286
 
287
- private buildInsertQuery<T extends object>(params: InsertQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
287
+ private buildInsertQuery<T extends keyof S>(params: InsertQueryParams<S, T>): BuildQueryResult<KeyValueQueryResult> {
288
288
  const key = this.extractKeyFromData(params.data);
289
289
 
290
290
  return {
291
291
  query: {
292
- bucket: params.tableName,
292
+ bucket: params.tableName as string,
293
293
  operation: 'set',
294
294
  key: key || 'default',
295
295
  value: params.data
@@ -298,12 +298,12 @@ export class RedisDialect extends Dialect<RedisDialectConfig, KeyValueQueryResul
298
298
  };
299
299
  }
300
300
 
301
- private buildUpdateQuery<T extends object>(params: UpdateQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
301
+ private buildUpdateQuery<T extends keyof S>(params: UpdateQueryParams<S, T>): BuildQueryResult<KeyValueQueryResult> {
302
302
  const key = this.extractKeyFromCondition(params.conditions);
303
303
 
304
304
  return {
305
305
  query: {
306
- bucket: params.tableName,
306
+ bucket: params.tableName as string,
307
307
  operation: 'set',
308
308
  key: key || 'default',
309
309
  value: params.update
@@ -312,12 +312,12 @@ export class RedisDialect extends Dialect<RedisDialectConfig, KeyValueQueryResul
312
312
  };
313
313
  }
314
314
 
315
- private buildDeleteQuery<T extends object>(params: DeleteQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
315
+ private buildDeleteQuery<T extends keyof S>(params: DeleteQueryParams<S, T>): BuildQueryResult<KeyValueQueryResult> {
316
316
  const key = this.extractKeyFromCondition(params.conditions);
317
317
 
318
318
  return {
319
319
  query: {
320
- bucket: params.tableName,
320
+ bucket: params.tableName as string,
321
321
  operation: 'delete',
322
322
  key: key || 'default'
323
323
  },
@@ -325,30 +325,30 @@ export class RedisDialect extends Dialect<RedisDialectConfig, KeyValueQueryResul
325
325
  };
326
326
  }
327
327
 
328
- private buildAlterQuery<T extends object>(params: AlterQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
328
+ private buildAlterQuery<T extends keyof S>(params: AlterQueryParams<S, T>): BuildQueryResult<KeyValueQueryResult> {
329
329
  return {
330
330
  query: {
331
- bucket: params.tableName,
331
+ bucket: params.tableName as string,
332
332
  operation: 'keys'
333
333
  },
334
334
  params: [params.alterations]
335
335
  };
336
336
  }
337
337
 
338
- private buildDropTableQuery<T extends object>(params: DropTableQueryParams<T>): BuildQueryResult<KeyValueQueryResult> {
338
+ private buildDropTableQuery<T extends keyof S>(params: DropTableQueryParams<S, T>): BuildQueryResult<KeyValueQueryResult> {
339
339
  return {
340
340
  query: {
341
- bucket: params.tableName,
341
+ bucket: params.tableName as string,
342
342
  operation: 'clear'
343
343
  },
344
344
  params: []
345
345
  };
346
346
  }
347
347
 
348
- private buildDropIndexQuery(params: DropIndexQueryParams): BuildQueryResult<KeyValueQueryResult> {
348
+ private buildDropIndexQuery<T extends keyof S>(params: DropIndexQueryParams<S, T>): BuildQueryResult<KeyValueQueryResult> {
349
349
  return {
350
350
  query: {
351
- bucket: params.tableName,
351
+ bucket: params.tableName as string,
352
352
  operation: 'keys'
353
353
  },
354
354
  params: [params.indexName]
@@ -592,7 +592,9 @@ export class RedisDialect extends Dialect<RedisDialectConfig, KeyValueQueryResul
592
592
  };
593
593
  }
594
594
  }
595
-
596
- Registry.register('redis', (config: RedisDialectConfig, definitions?: Database.Definitions<Record<string, object>>) => {
597
- return new KeyValueDatabase(new RedisDialect(config), definitions);
598
- });
595
+ export class Redis<S extends Record<string, object> = Record<string, object>> extends KeyValueDatabase<RedisDialectConfig, S> {
596
+ constructor(config: RedisDialectConfig, definitions?: Database.DefinitionObj<S>) {
597
+ super(new RedisDialect<S>(config), definitions);
598
+ }
599
+ }
600
+ Registry.register('redis', Redis);