oak-db 3.3.10 → 3.3.12

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.
@@ -1,12 +1,12 @@
1
- export type MySQLConfiguration = {
2
- host: string;
3
- user: string;
4
- password: string;
5
- database: string;
6
- charset: 'utf8mb4_general_ci';
7
- connectionLimit: number;
8
- port?: number;
9
- };
10
- export type Configuration = {
11
- mysql: MySQLConfiguration;
12
- };
1
+ export type MySQLConfiguration = {
2
+ host: string;
3
+ user: string;
4
+ password: string;
5
+ database: string;
6
+ charset: 'utf8mb4_general_ci';
7
+ connectionLimit: number;
8
+ port?: number;
9
+ };
10
+ export type Configuration = {
11
+ mysql: MySQLConfiguration;
12
+ };
@@ -1,2 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,31 @@
1
+ import { Pool, PoolClient, QueryResult, QueryResultRow } from 'pg';
2
+ import { TxnOption } from 'oak-domain/lib/types';
3
+ import { PostgreSQLConfiguration } from './types/Configuration';
4
+ export declare class PostgreSQLConnector {
5
+ pool: Pool;
6
+ configuration: PostgreSQLConfiguration;
7
+ txnDict: Record<string, PoolClient>;
8
+ constructor(configuration: PostgreSQLConfiguration);
9
+ connect(): void;
10
+ disconnect(): Promise<void>;
11
+ startTransaction(option?: TxnOption): Promise<string>;
12
+ /**
13
+ * 映射隔离级别到 PostgreSQL 语法
14
+ */
15
+ private mapIsolationLevel;
16
+ exec(sql: string, txn?: string): Promise<[QueryResultRow[], QueryResult]>;
17
+ commitTransaction(txn: string): Promise<void>;
18
+ rollbackTransaction(txn: string): Promise<void>;
19
+ /**
20
+ * 执行多条 SQL 语句(用于初始化等场景)
21
+ */
22
+ execBatch(sqls: string[], txn?: string): Promise<void>;
23
+ /**
24
+ * 获取连接池状态
25
+ */
26
+ getPoolStatus(): {
27
+ total: number;
28
+ idle: number;
29
+ waiting: number;
30
+ };
31
+ }
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgreSQLConnector = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const pg_1 = require("pg");
6
+ const uuid_1 = require("uuid");
7
+ const assert_1 = tslib_1.__importDefault(require("assert"));
8
+ class PostgreSQLConnector {
9
+ pool;
10
+ configuration;
11
+ txnDict;
12
+ constructor(configuration) {
13
+ this.configuration = configuration;
14
+ this.txnDict = {};
15
+ this.pool = new pg_1.Pool(configuration);
16
+ // 错误处理
17
+ this.pool.on('error', (err) => {
18
+ console.error('PostgreSQL pool error:', err);
19
+ });
20
+ }
21
+ connect() {
22
+ // pg Pool 是惰性连接,不需要显式连接
23
+ // 但可以在这里进行连接测试
24
+ }
25
+ async disconnect() {
26
+ // 先获取所有事务ID的快照
27
+ const txnIds = Object.keys(this.txnDict);
28
+ for (const txnId of txnIds) {
29
+ try {
30
+ await this.rollbackTransaction(txnId);
31
+ }
32
+ catch (e) {
33
+ console.error(`Failed to rollback transaction ${txnId}:`, e);
34
+ }
35
+ }
36
+ await this.pool.end();
37
+ }
38
+ async startTransaction(option) {
39
+ const startInner = async () => {
40
+ const connection = await this.pool.connect();
41
+ // 添加:检测连接是否已被其他事务占用
42
+ for (const txn2 in this.txnDict) {
43
+ if (this.txnDict[txn2] === connection) {
44
+ return new Promise((resolve) => {
45
+ this.pool.on('release', () => resolve(startInner()));
46
+ });
47
+ }
48
+ }
49
+ try {
50
+ let beginStmt = 'BEGIN';
51
+ if (option?.isolationLevel) {
52
+ // PostgreSQL 隔离级别:
53
+ // READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE
54
+ // 注意: PostgreSQL 的 READ UNCOMMITTED 行为等同于 READ COMMITTED
55
+ const level = this.mapIsolationLevel(option.isolationLevel);
56
+ beginStmt = `BEGIN ISOLATION LEVEL ${level}`;
57
+ }
58
+ await connection.query(beginStmt);
59
+ const id = (0, uuid_1.v4)();
60
+ this.txnDict[id] = connection;
61
+ return id;
62
+ }
63
+ catch (error) {
64
+ // 如果启动事务失败,释放连接
65
+ connection.release();
66
+ throw error;
67
+ }
68
+ };
69
+ return startInner();
70
+ }
71
+ /**
72
+ * 映射隔离级别到 PostgreSQL 语法
73
+ */
74
+ mapIsolationLevel(level) {
75
+ const levelUpper = level.toUpperCase().replace(/[-_]/g, ' '); // 同时处理 - 和 _
76
+ const validLevels = [
77
+ 'READ UNCOMMITTED',
78
+ 'READ COMMITTED',
79
+ 'REPEATABLE READ',
80
+ 'SERIALIZABLE'
81
+ ];
82
+ if (validLevels.includes(levelUpper)) {
83
+ return levelUpper;
84
+ }
85
+ // 默认使用 READ COMMITTED
86
+ console.warn(`Unknown isolation level "${level}", using READ COMMITTED`);
87
+ return 'READ COMMITTED';
88
+ }
89
+ async exec(sql, txn) {
90
+ if (process.env.NODE_ENV === 'development') {
91
+ // console.log(`SQL: ${sql}; \n`);
92
+ }
93
+ try {
94
+ let result;
95
+ if (txn) {
96
+ const connection = this.txnDict[txn];
97
+ (0, assert_1.default)(connection, `Transaction ${txn} not found`);
98
+ result = await connection.query(sql);
99
+ }
100
+ else {
101
+ result = await this.pool.query(sql);
102
+ }
103
+ // 返回格式与 mysql2 兼容: [rows, fields/result]
104
+ return [result.rows, result];
105
+ }
106
+ catch (error) {
107
+ // 增强错误信息
108
+ const enhancedError = new Error(`PostgreSQL query failed: ${error.message}\nSQL: ${sql.slice(0, 500)}${sql.length > 500 ? '...' : ''}`);
109
+ enhancedError.originalError = error;
110
+ enhancedError.sql = sql;
111
+ throw enhancedError;
112
+ }
113
+ }
114
+ async commitTransaction(txn) {
115
+ const connection = this.txnDict[txn];
116
+ (0, assert_1.default)(connection, `Transaction ${txn} not found`);
117
+ try {
118
+ await connection.query('COMMIT');
119
+ }
120
+ finally {
121
+ delete this.txnDict[txn];
122
+ connection.release();
123
+ }
124
+ }
125
+ async rollbackTransaction(txn) {
126
+ const connection = this.txnDict[txn];
127
+ (0, assert_1.default)(connection, `Transaction ${txn} not found`);
128
+ try {
129
+ await connection.query('ROLLBACK');
130
+ }
131
+ finally {
132
+ delete this.txnDict[txn];
133
+ connection.release();
134
+ }
135
+ }
136
+ /**
137
+ * 执行多条 SQL 语句(用于初始化等场景)
138
+ */
139
+ async execBatch(sqls, txn) {
140
+ for (const sql of sqls) {
141
+ if (sql.trim()) {
142
+ await this.exec(sql, txn);
143
+ }
144
+ }
145
+ }
146
+ /**
147
+ * 获取连接池状态
148
+ */
149
+ getPoolStatus() {
150
+ return {
151
+ total: this.pool.totalCount,
152
+ idle: this.pool.idleCount,
153
+ waiting: this.pool.waitingCount
154
+ };
155
+ }
156
+ }
157
+ exports.PostgreSQLConnector = PostgreSQLConnector;
@@ -0,0 +1,38 @@
1
+ import { EntityDict, OperateOption, OperationResult, TxnOption, StorageSchema, SelectOption, AggregationResult } from 'oak-domain/lib/types';
2
+ import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
3
+ import { CascadeStore } from 'oak-domain/lib/store/CascadeStore';
4
+ import { PostgreSQLConfiguration } from './types/Configuration';
5
+ import { PostgreSQLConnector } from './connector';
6
+ import { PostgreSQLTranslator, PostgreSQLSelectOption, PostgreSQLOperateOption } from './translator';
7
+ import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
8
+ import { SyncContext } from 'oak-domain/lib/store/SyncRowStore';
9
+ import { CreateEntityOption } from '../types/Translator';
10
+ import { DbStore } from '../types/dbStore';
11
+ export declare class PostgreSQLStore<ED extends EntityDict & BaseEntityDict, Cxt extends AsyncContext<ED>> extends CascadeStore<ED> implements DbStore<ED, Cxt> {
12
+ protected countAbjointRow<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): number;
13
+ protected aggregateAbjointRowSync<T extends keyof ED, OP extends SelectOption, Cxt extends SyncContext<ED>>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): AggregationResult<ED[T]['Schema']>;
14
+ protected selectAbjointRow<T extends keyof ED, OP extends SelectOption>(entity: T, selection: ED[T]['Selection'], context: SyncContext<ED>, option: OP): Partial<ED[T]['Schema']>[];
15
+ protected updateAbjointRow<T extends keyof ED, OP extends OperateOption>(entity: T, operation: ED[T]['Operation'], context: SyncContext<ED>, option: OP): number;
16
+ exec(script: string, txnId?: string): Promise<void>;
17
+ connector: PostgreSQLConnector;
18
+ translator: PostgreSQLTranslator<ED>;
19
+ constructor(storageSchema: StorageSchema<ED>, configuration: PostgreSQLConfiguration);
20
+ checkRelationAsync<T extends keyof ED, Cxt extends AsyncContext<ED>>(entity: T, operation: Omit<ED[T]['Operation'] | ED[T]['Selection'], 'id'>, context: Cxt): Promise<void>;
21
+ protected aggregateAbjointRowAsync<T extends keyof ED, OP extends SelectOption, Cxt extends AsyncContext<ED>>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): Promise<AggregationResult<ED[T]['Schema']>>;
22
+ aggregate<T extends keyof ED, OP extends SelectOption>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): Promise<AggregationResult<ED[T]['Schema']>>;
23
+ protected supportManyToOneJoin(): boolean;
24
+ protected supportMultipleCreate(): boolean;
25
+ private formResult;
26
+ protected selectAbjointRowAsync<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context: AsyncContext<ED>, option?: PostgreSQLSelectOption): Promise<Partial<ED[T]['Schema']>[]>;
27
+ protected updateAbjointRowAsync<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: AsyncContext<ED>, option?: PostgreSQLOperateOption): Promise<number>;
28
+ operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OperateOption): Promise<OperationResult<ED>>;
29
+ select<T extends keyof ED>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: SelectOption): Promise<Partial<ED[T]['Schema']>[]>;
30
+ protected countAbjointRowAsync<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: AsyncContext<ED>, option: SelectOption): Promise<number>;
31
+ count<T extends keyof ED>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: SelectOption): Promise<number>;
32
+ begin(option?: TxnOption): Promise<string>;
33
+ commit(txnId: string): Promise<void>;
34
+ rollback(txnId: string): Promise<void>;
35
+ connect(): Promise<void>;
36
+ disconnect(): Promise<void>;
37
+ initialize(option: CreateEntityOption): Promise<void>;
38
+ }
@@ -0,0 +1,329 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgreSQLStore = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const CascadeStore_1 = require("oak-domain/lib/store/CascadeStore");
6
+ const connector_1 = require("./connector");
7
+ const translator_1 = require("./translator");
8
+ const lodash_1 = require("lodash");
9
+ const assert_1 = tslib_1.__importDefault(require("assert"));
10
+ const relation_1 = require("oak-domain/lib/store/relation");
11
+ function convertGeoTextToObject(geoText) {
12
+ if (geoText.startsWith('POINT')) {
13
+ const coord = geoText.match(/(-?\d+\.?\d*)/g);
14
+ (0, assert_1.default)(coord && coord.length === 2);
15
+ return {
16
+ type: 'point',
17
+ coordinate: coord.map(ele => parseFloat(ele)),
18
+ };
19
+ }
20
+ else if (geoText.startsWith('LINESTRING')) {
21
+ const coordsMatch = geoText.match(/\(([^)]+)\)/);
22
+ if (coordsMatch) {
23
+ const points = coordsMatch[1].split(',').map(p => {
24
+ const [x, y] = p.trim().split(/\s+/).map(parseFloat);
25
+ return [x, y];
26
+ });
27
+ return {
28
+ type: 'path',
29
+ coordinate: points,
30
+ };
31
+ }
32
+ }
33
+ else if (geoText.startsWith('POLYGON')) {
34
+ const ringsMatch = geoText.match(/\(\(([^)]+)\)\)/g);
35
+ if (ringsMatch) {
36
+ const rings = ringsMatch.map(ring => {
37
+ const coordStr = ring.replace(/[()]/g, '');
38
+ return coordStr.split(',').map(p => {
39
+ const [x, y] = p.trim().split(/\s+/).map(parseFloat);
40
+ return [x, y];
41
+ });
42
+ });
43
+ return {
44
+ type: 'polygon',
45
+ coordinate: rings,
46
+ };
47
+ }
48
+ }
49
+ throw new Error(`Unsupported geometry type: ${geoText.slice(0, 50)}`);
50
+ }
51
+ class PostgreSQLStore extends CascadeStore_1.CascadeStore {
52
+ countAbjointRow(entity, selection, context, option) {
53
+ throw new Error('PostgreSQL store 不支持同步取数据');
54
+ }
55
+ aggregateAbjointRowSync(entity, aggregation, context, option) {
56
+ throw new Error('PostgreSQL store 不支持同步取数据');
57
+ }
58
+ selectAbjointRow(entity, selection, context, option) {
59
+ throw new Error('PostgreSQL store 不支持同步取数据');
60
+ }
61
+ updateAbjointRow(entity, operation, context, option) {
62
+ throw new Error('PostgreSQL store 不支持同步更新数据');
63
+ }
64
+ async exec(script, txnId) {
65
+ await this.connector.exec(script, txnId);
66
+ }
67
+ connector;
68
+ translator;
69
+ constructor(storageSchema, configuration) {
70
+ super(storageSchema);
71
+ this.connector = new connector_1.PostgreSQLConnector(configuration);
72
+ this.translator = new translator_1.PostgreSQLTranslator(storageSchema);
73
+ }
74
+ checkRelationAsync(entity, operation, context) {
75
+ throw new Error('Method not implemented.');
76
+ }
77
+ async aggregateAbjointRowAsync(entity, aggregation, context, option) {
78
+ const sql = this.translator.translateAggregate(entity, aggregation, option);
79
+ const result = await this.connector.exec(sql, context.getCurrentTxnId());
80
+ return this.formResult(entity, result[0]);
81
+ }
82
+ aggregate(entity, aggregation, context, option) {
83
+ return this.aggregateAsync(entity, aggregation, context, option);
84
+ }
85
+ supportManyToOneJoin() {
86
+ return true;
87
+ }
88
+ supportMultipleCreate() {
89
+ return true;
90
+ }
91
+ formResult(entity, result) {
92
+ const schema = this.getSchema();
93
+ function resolveAttribute(entity2, r, attr, value) {
94
+ const { attributes, view } = schema[entity2];
95
+ if (!view) {
96
+ const i = attr.indexOf(".");
97
+ if (i !== -1) {
98
+ const attrHead = attr.slice(0, i);
99
+ const attrTail = attr.slice(i + 1);
100
+ const rel = (0, relation_1.judgeRelation)(schema, entity2, attrHead);
101
+ if (rel === 1) {
102
+ (0, lodash_1.set)(r, attr, value);
103
+ }
104
+ else {
105
+ if (!r[attrHead]) {
106
+ r[attrHead] = {};
107
+ }
108
+ if (rel === 0) {
109
+ resolveAttribute(entity2, r[attrHead], attrTail, value);
110
+ }
111
+ else if (rel === 2) {
112
+ resolveAttribute(attrHead, r[attrHead], attrTail, value);
113
+ }
114
+ else {
115
+ (0, assert_1.default)(typeof rel === 'string');
116
+ resolveAttribute(rel, r[attrHead], attrTail, value);
117
+ }
118
+ }
119
+ }
120
+ else if (attributes[attr]) {
121
+ const { type } = attributes[attr];
122
+ switch (type) {
123
+ case 'date':
124
+ case 'time': {
125
+ if (value instanceof Date) {
126
+ r[attr] = value.valueOf();
127
+ }
128
+ else {
129
+ r[attr] = value;
130
+ }
131
+ break;
132
+ }
133
+ case 'geometry': {
134
+ if (typeof value === 'string') {
135
+ r[attr] = convertGeoTextToObject(value);
136
+ }
137
+ else {
138
+ r[attr] = value;
139
+ }
140
+ break;
141
+ }
142
+ case 'object':
143
+ case 'array': {
144
+ // PostgreSQL jsonb 直接返回对象,不需要 parse
145
+ if (typeof value === 'string') {
146
+ r[attr] = JSON.parse(value);
147
+ }
148
+ else {
149
+ r[attr] = value;
150
+ }
151
+ break;
152
+ }
153
+ case 'function': {
154
+ if (typeof value === 'string') {
155
+ r[attr] = `return ${Buffer.from(value, 'base64').toString()}`;
156
+ }
157
+ else {
158
+ r[attr] = value;
159
+ }
160
+ break;
161
+ }
162
+ case 'bool':
163
+ case 'boolean': {
164
+ // PostgreSQL 直接返回 boolean 类型
165
+ r[attr] = value;
166
+ break;
167
+ }
168
+ case 'decimal': {
169
+ // PostgreSQL numeric 类型可能返回字符串
170
+ if (typeof value === 'string') {
171
+ r[attr] = parseFloat(value);
172
+ }
173
+ else {
174
+ (0, assert_1.default)(value === null || typeof value === 'number');
175
+ r[attr] = value;
176
+ }
177
+ break;
178
+ }
179
+ // TODO: 这里和mysql统一行为,ref类型的字符串去除前后空格
180
+ case "char":
181
+ case "ref": {
182
+ if (value) {
183
+ (0, assert_1.default)(typeof value === 'string');
184
+ r[attr] = value.trim();
185
+ }
186
+ else {
187
+ r[attr] = value;
188
+ }
189
+ break;
190
+ }
191
+ default: {
192
+ r[attr] = value;
193
+ }
194
+ }
195
+ }
196
+ else {
197
+ // TODO: 这里和mysql统一行为,id字段为char类型时,去除后面的空格
198
+ if (value && typeof value === 'string') {
199
+ if (attr === 'id') {
200
+ r[attr] = value.trim();
201
+ }
202
+ else if (attr.startsWith("#count")) {
203
+ // PostgreSQL count 返回字符串
204
+ r[attr] = parseInt(value, 10);
205
+ }
206
+ else {
207
+ r[attr] = value;
208
+ }
209
+ }
210
+ else {
211
+ r[attr] = value;
212
+ }
213
+ }
214
+ }
215
+ else {
216
+ (0, lodash_1.assign)(r, { [attr]: value });
217
+ }
218
+ }
219
+ function removeNullObjects(r, e) {
220
+ for (let attr in r) {
221
+ const rel = (0, relation_1.judgeRelation)(schema, e, attr);
222
+ if (rel === 2) {
223
+ if (r[attr].id === null) {
224
+ (0, assert_1.default)(schema[e].toModi || r.entity !== attr);
225
+ delete r[attr];
226
+ continue;
227
+ }
228
+ removeNullObjects(r[attr], attr);
229
+ }
230
+ else if (typeof rel === 'string') {
231
+ if (r[attr].id === null) {
232
+ (0, assert_1.default)(schema[e].toModi || r[`${attr}Id`] === null, `对象${String(e)}取数据时,发现其外键找不到目标对象,rowId是${r.id},其外键${attr}Id值为${r[`${attr}Id`]}`);
233
+ delete r[attr];
234
+ continue;
235
+ }
236
+ removeNullObjects(r[attr], rel);
237
+ }
238
+ }
239
+ }
240
+ function formSingleRow(r) {
241
+ let result2 = {};
242
+ for (let attr in r) {
243
+ const value = r[attr];
244
+ resolveAttribute(entity, result2, attr, value);
245
+ }
246
+ removeNullObjects(result2, entity);
247
+ return result2;
248
+ }
249
+ if (result instanceof Array) {
250
+ return result.map(r => formSingleRow(r));
251
+ }
252
+ return formSingleRow(result);
253
+ }
254
+ async selectAbjointRowAsync(entity, selection, context, option) {
255
+ const sql = this.translator.translateSelect(entity, selection, option);
256
+ const result = await this.connector.exec(sql, context.getCurrentTxnId());
257
+ return this.formResult(entity, result[0]);
258
+ }
259
+ async updateAbjointRowAsync(entity, operation, context, option) {
260
+ const { translator, connector } = this;
261
+ const { action } = operation;
262
+ const txn = context.getCurrentTxnId();
263
+ switch (action) {
264
+ case 'create': {
265
+ const { data } = operation;
266
+ const sql = translator.translateInsert(entity, data instanceof Array ? data : [data]);
267
+ const result = await connector.exec(sql, txn);
268
+ // PostgreSQL QueryResult.rowCount
269
+ return result[1].rowCount || 0;
270
+ }
271
+ case 'remove': {
272
+ const sql = translator.translateRemove(entity, operation, option);
273
+ const result = await connector.exec(sql, txn);
274
+ return result[1].rowCount || 0;
275
+ }
276
+ default: {
277
+ (0, assert_1.default)(!['select', 'download', 'stat'].includes(action));
278
+ const sql = translator.translateUpdate(entity, operation, option);
279
+ const result = await connector.exec(sql, txn);
280
+ return result[1].rowCount || 0;
281
+ }
282
+ }
283
+ }
284
+ async operate(entity, operation, context, option) {
285
+ const { action } = operation;
286
+ (0, assert_1.default)(!['select', 'download', 'stat'].includes(action), '不支持使用 select operation');
287
+ return await super.operateAsync(entity, operation, context, option);
288
+ }
289
+ async select(entity, selection, context, option) {
290
+ return await super.selectAsync(entity, selection, context, option);
291
+ }
292
+ async countAbjointRowAsync(entity, selection, context, option) {
293
+ const sql = this.translator.translateCount(entity, selection, option);
294
+ const result = await this.connector.exec(sql, context.getCurrentTxnId());
295
+ // PostgreSQL 返回的 count 是 string 类型(bigint)
296
+ const cnt = result[0][0]?.cnt;
297
+ return typeof cnt === 'string' ? parseInt(cnt, 10) : (cnt || 0);
298
+ }
299
+ async count(entity, selection, context, option) {
300
+ return this.countAsync(entity, selection, context, option);
301
+ }
302
+ async begin(option) {
303
+ return await this.connector.startTransaction(option);
304
+ }
305
+ async commit(txnId) {
306
+ await this.connector.commitTransaction(txnId);
307
+ }
308
+ async rollback(txnId) {
309
+ await this.connector.rollbackTransaction(txnId);
310
+ }
311
+ async connect() {
312
+ await this.connector.connect();
313
+ }
314
+ async disconnect() {
315
+ await this.connector.disconnect();
316
+ }
317
+ async initialize(option) {
318
+ const schema = this.getSchema();
319
+ // 可选:先创建 PostGIS 扩展
320
+ // await this.connector.exec('CREATE EXTENSION IF NOT EXISTS postgis;');
321
+ for (const entity in schema) {
322
+ const sqls = this.translator.translateCreateEntity(entity, option);
323
+ for (const sql of sqls) {
324
+ await this.connector.exec(sql);
325
+ }
326
+ }
327
+ }
328
+ }
329
+ exports.PostgreSQLStore = PostgreSQLStore;
@@ -0,0 +1,83 @@
1
+ import { EntityDict, Q_FullTextValue, RefOrExpression, Ref, StorageSchema } from "oak-domain/lib/types";
2
+ import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
3
+ import { DataType } from "oak-domain/lib/types/schema/DataTypes";
4
+ import { SqlOperateOption, SqlSelectOption, SqlTranslator } from "../sqlTranslator";
5
+ import { CreateEntityOption } from '../types/Translator';
6
+ export interface PostgreSQLSelectOption extends SqlSelectOption {
7
+ }
8
+ export interface PostgreSQLOperateOption extends SqlOperateOption {
9
+ }
10
+ export declare class PostgreSQLTranslator<ED extends EntityDict & BaseEntityDict> extends SqlTranslator<ED> {
11
+ private getEnumTypeName;
12
+ /**
13
+ * 将 MySQL 风格的 JSON 路径转换为 PostgreSQL 路径数组格式
14
+ * 例如: ".foo.bar[0].baz" -> '{foo,bar,0,baz}'
15
+ */
16
+ private convertJsonPath;
17
+ /**
18
+ * 生成 PostgreSQL JSON 访问表达式
19
+ * @param column 列名(包含别名)
20
+ * @param path JSON 路径
21
+ * @param asText 是否返回文本(使用 #>> 而不是 #>)
22
+ */
23
+ private buildJsonAccessor;
24
+ protected getDefaultSelectFilter(alias: string, option?: PostgreSQLSelectOption): string;
25
+ private makeUpSchema;
26
+ constructor(schema: StorageSchema<ED>);
27
+ static supportedDataTypes: DataType[];
28
+ static spatialTypes: DataType[];
29
+ static withLengthDataTypes: DataType[];
30
+ static withPrecisionDataTypes: DataType[];
31
+ static withScaleDataTypes: DataType[];
32
+ static dataTypeDefaults: Record<string, any>;
33
+ maxAliasLength: number;
34
+ private populateDataTypeDef;
35
+ /**
36
+ * PostgreSQL 字符串值转义
37
+ * 防御SQL注入
38
+ */
39
+ escapeStringValue(value: string): string;
40
+ /**
41
+ * LIKE 模式转义
42
+ * 转义 LIKE 语句中的特殊字符
43
+ */
44
+ escapeLikePattern(value: string): string;
45
+ /**
46
+ * PostgreSQL 标识符转义
47
+ * 用于表名、列名等
48
+ */
49
+ escapeIdentifier(identifier: string): string;
50
+ /**
51
+ * tsquery 搜索词转义
52
+ */
53
+ escapeTsQueryValue(value: string): string;
54
+ quoteIdentifier(identifier: string): string;
55
+ protected translateAttrProjection(dataType: DataType, alias: string, attr: string): string;
56
+ protected translateObjectPredicate(predicate: Record<string, any>, alias: string, attr: string): string;
57
+ protected translateObjectProjection(projection: Record<string, any>, alias: string, attr: string, prefix: string): string;
58
+ protected translateAttrValue(dataType: DataType | Ref, value: any): string;
59
+ protected translateFullTextSearch<T extends keyof ED>(value: Q_FullTextValue, entity: T, alias: string): string;
60
+ translateCreateEntity<T extends keyof ED>(entity: T, options?: CreateEntityOption): string[];
61
+ private translateFnName;
62
+ private translateAttrInExpression;
63
+ protected translateExpression<T extends keyof ED>(entity: T, alias: string, expression: RefOrExpression<keyof ED[T]["OpSchema"]>, refDict: Record<string, [string, keyof ED]>): string;
64
+ protected populateSelectStmt<T extends keyof ED>(projectionText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, groupByText?: string, indexFrom?: number, count?: number, option?: PostgreSQLSelectOption): string;
65
+ translateUpdate<T extends keyof ED, OP extends SqlOperateOption>(entity: T, operation: ED[T]['Update'], option?: OP): string;
66
+ translateRemove<T extends keyof ED, OP extends SqlOperateOption>(entity: T, operation: ED[T]['Remove'], option?: OP): string;
67
+ /**
68
+ * PostgreSQL专用的结构化JOIN分析
69
+ * 返回结构化的JOIN信息,而不是拼接好的FROM字符串
70
+ */
71
+ private analyzeJoinStructured;
72
+ /**
73
+ * 构建JOIN条件(用于UPDATE/DELETE的WHERE子句)
74
+ */
75
+ private buildJoinConditions;
76
+ /**
77
+ * 生成 PostgreSQL UPSERT 语句
78
+ * INSERT ... ON CONFLICT (key) DO UPDATE SET ...
79
+ */
80
+ translateUpsert<T extends keyof ED>(entity: T, data: ED[T]['CreateMulti']['data'], conflictKeys: string[], updateAttrs?: string[]): string;
81
+ protected populateUpdateStmt(updateText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: PostgreSQLOperateOption): string;
82
+ protected populateRemoveStmt(updateText: string, fromText: string, aliasDict: Record<string, string>, filterText: string, sorterText?: string, indexFrom?: number, count?: number, option?: PostgreSQLOperateOption): string;
83
+ }