158 lines
5.2 KiB
JavaScript
158 lines
5.2 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.PostgreSQLConnector = void 0;
|
|
const tslib_1 = require("tslib");
|
|
const pg_1 = require("pg");
|
|
const uuid_1 = require("uuid");
|
|
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
class PostgreSQLConnector {
|
|
pool;
|
|
configuration;
|
|
txnDict;
|
|
constructor(configuration) {
|
|
this.configuration = configuration;
|
|
this.txnDict = {};
|
|
this.pool = new pg_1.Pool(configuration);
|
|
// 错误处理
|
|
this.pool.on('error', (err) => {
|
|
console.error('PostgreSQL pool error:', err);
|
|
});
|
|
}
|
|
connect() {
|
|
// pg Pool 是惰性连接,不需要显式连接
|
|
// 但可以在这里进行连接测试
|
|
}
|
|
async disconnect() {
|
|
// 先获取所有事务ID的快照
|
|
const txnIds = Object.keys(this.txnDict);
|
|
for (const txnId of txnIds) {
|
|
try {
|
|
await this.rollbackTransaction(txnId);
|
|
}
|
|
catch (e) {
|
|
console.error(`Failed to rollback transaction ${txnId}:`, e);
|
|
}
|
|
}
|
|
await this.pool.end();
|
|
}
|
|
async startTransaction(option) {
|
|
const startInner = async () => {
|
|
const connection = await this.pool.connect();
|
|
// 添加:检测连接是否已被其他事务占用
|
|
for (const txn2 in this.txnDict) {
|
|
if (this.txnDict[txn2] === connection) {
|
|
return new Promise((resolve) => {
|
|
this.pool.on('release', () => resolve(startInner()));
|
|
});
|
|
}
|
|
}
|
|
try {
|
|
let beginStmt = 'BEGIN';
|
|
if (option?.isolationLevel) {
|
|
// PostgreSQL 隔离级别:
|
|
// READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE
|
|
// 注意: PostgreSQL 的 READ UNCOMMITTED 行为等同于 READ COMMITTED
|
|
const level = this.mapIsolationLevel(option.isolationLevel);
|
|
beginStmt = `BEGIN ISOLATION LEVEL ${level}`;
|
|
}
|
|
await connection.query(beginStmt);
|
|
const id = (0, uuid_1.v4)();
|
|
this.txnDict[id] = connection;
|
|
return id;
|
|
}
|
|
catch (error) {
|
|
// 如果启动事务失败,释放连接
|
|
connection.release();
|
|
throw error;
|
|
}
|
|
};
|
|
return startInner();
|
|
}
|
|
/**
|
|
* 映射隔离级别到 PostgreSQL 语法
|
|
*/
|
|
mapIsolationLevel(level) {
|
|
const levelUpper = level.toUpperCase().replace(/[-_]/g, ' '); // 同时处理 - 和 _
|
|
const validLevels = [
|
|
'READ UNCOMMITTED',
|
|
'READ COMMITTED',
|
|
'REPEATABLE READ',
|
|
'SERIALIZABLE'
|
|
];
|
|
if (validLevels.includes(levelUpper)) {
|
|
return levelUpper;
|
|
}
|
|
// 默认使用 READ COMMITTED
|
|
console.warn(`Unknown isolation level "${level}", using READ COMMITTED`);
|
|
return 'READ COMMITTED';
|
|
}
|
|
async exec(sql, txn) {
|
|
if (process.env.NODE_ENV === 'development') {
|
|
// console.log('SQL:', sql);
|
|
}
|
|
try {
|
|
let result;
|
|
if (txn) {
|
|
const connection = this.txnDict[txn];
|
|
(0, assert_1.default)(connection, `Transaction ${txn} not found`);
|
|
result = await connection.query(sql);
|
|
}
|
|
else {
|
|
result = await this.pool.query(sql);
|
|
}
|
|
// 返回格式与 mysql2 兼容: [rows, fields/result]
|
|
return [result.rows, result];
|
|
}
|
|
catch (error) {
|
|
// 增强错误信息
|
|
const enhancedError = new Error(`PostgreSQL query failed: ${error.message}\nSQL: ${sql.slice(0, 500)}${sql.length > 500 ? '...' : ''}`);
|
|
enhancedError.originalError = error;
|
|
enhancedError.sql = sql;
|
|
throw enhancedError;
|
|
}
|
|
}
|
|
async commitTransaction(txn) {
|
|
const connection = this.txnDict[txn];
|
|
(0, assert_1.default)(connection, `Transaction ${txn} not found`);
|
|
try {
|
|
await connection.query('COMMIT');
|
|
}
|
|
finally {
|
|
delete this.txnDict[txn];
|
|
connection.release();
|
|
}
|
|
}
|
|
async rollbackTransaction(txn) {
|
|
const connection = this.txnDict[txn];
|
|
(0, assert_1.default)(connection, `Transaction ${txn} not found`);
|
|
try {
|
|
await connection.query('ROLLBACK');
|
|
}
|
|
finally {
|
|
delete this.txnDict[txn];
|
|
connection.release();
|
|
}
|
|
}
|
|
/**
|
|
* 执行多条 SQL 语句(用于初始化等场景)
|
|
*/
|
|
async execBatch(sqls, txn) {
|
|
for (const sql of sqls) {
|
|
if (sql.trim()) {
|
|
await this.exec(sql, txn);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* 获取连接池状态
|
|
*/
|
|
getPoolStatus() {
|
|
return {
|
|
total: this.pool.totalCount,
|
|
idle: this.pool.idleCount,
|
|
waiting: this.pool.waitingCount
|
|
};
|
|
}
|
|
}
|
|
exports.PostgreSQLConnector = PostgreSQLConnector;
|