@mostajs/orm 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +548 -0
- package/dist/core/base-repository.d.ts +26 -0
- package/dist/core/base-repository.js +82 -0
- package/dist/core/config.d.ts +62 -0
- package/dist/core/config.js +116 -0
- package/dist/core/errors.d.ts +30 -0
- package/dist/core/errors.js +49 -0
- package/dist/core/factory.d.ts +41 -0
- package/dist/core/factory.js +142 -0
- package/dist/core/normalizer.d.ts +9 -0
- package/dist/core/normalizer.js +19 -0
- package/dist/core/registry.d.ts +43 -0
- package/dist/core/registry.js +78 -0
- package/dist/core/types.d.ts +228 -0
- package/dist/core/types.js +5 -0
- package/dist/dialects/abstract-sql.dialect.d.ts +113 -0
- package/dist/dialects/abstract-sql.dialect.js +1071 -0
- package/dist/dialects/cockroachdb.dialect.d.ts +2 -0
- package/dist/dialects/cockroachdb.dialect.js +23 -0
- package/dist/dialects/db2.dialect.d.ts +2 -0
- package/dist/dialects/db2.dialect.js +190 -0
- package/dist/dialects/hana.dialect.d.ts +2 -0
- package/dist/dialects/hana.dialect.js +199 -0
- package/dist/dialects/hsqldb.dialect.d.ts +2 -0
- package/dist/dialects/hsqldb.dialect.js +114 -0
- package/dist/dialects/mariadb.dialect.d.ts +2 -0
- package/dist/dialects/mariadb.dialect.js +87 -0
- package/dist/dialects/mongo.dialect.d.ts +2 -0
- package/dist/dialects/mongo.dialect.js +480 -0
- package/dist/dialects/mssql.dialect.d.ts +27 -0
- package/dist/dialects/mssql.dialect.js +127 -0
- package/dist/dialects/mysql.dialect.d.ts +24 -0
- package/dist/dialects/mysql.dialect.js +101 -0
- package/dist/dialects/oracle.dialect.d.ts +2 -0
- package/dist/dialects/oracle.dialect.js +206 -0
- package/dist/dialects/postgres.dialect.d.ts +26 -0
- package/dist/dialects/postgres.dialect.js +105 -0
- package/dist/dialects/spanner.dialect.d.ts +2 -0
- package/dist/dialects/spanner.dialect.js +259 -0
- package/dist/dialects/sqlite.dialect.d.ts +2 -0
- package/dist/dialects/sqlite.dialect.js +1027 -0
- package/dist/dialects/sybase.dialect.d.ts +2 -0
- package/dist/dialects/sybase.dialect.js +119 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +26 -0
- package/docs/api-reference.md +1009 -0
- package/docs/dialects.md +673 -0
- package/docs/tutorial.md +846 -0
- package/package.json +91 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// MySQL Dialect — extends AbstractSqlDialect
|
|
2
|
+
// Equivalent to org.hibernate.dialect.MySQLDialect (Hibernate ORM 6.4)
|
|
3
|
+
// Driver: npm install mysql2
|
|
4
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
5
|
+
import { AbstractSqlDialect } from './abstract-sql.dialect.js';
|
|
6
|
+
// ============================================================
|
|
7
|
+
// Type Mapping — DAL FieldType → MySQL column type
|
|
8
|
+
// ============================================================
|
|
9
|
+
const MYSQL_TYPE_MAP = {
|
|
10
|
+
string: 'TEXT',
|
|
11
|
+
number: 'DOUBLE',
|
|
12
|
+
boolean: 'TINYINT(1)',
|
|
13
|
+
date: 'DATETIME',
|
|
14
|
+
json: 'JSON',
|
|
15
|
+
array: 'JSON',
|
|
16
|
+
};
|
|
17
|
+
// ============================================================
|
|
18
|
+
// MySQLDialect
|
|
19
|
+
// ============================================================
|
|
20
|
+
export class MySQLDialect extends AbstractSqlDialect {
|
|
21
|
+
dialectType = 'mysql';
|
|
22
|
+
pool = null;
|
|
23
|
+
// --- Abstract implementations ---
|
|
24
|
+
quoteIdentifier(name) {
|
|
25
|
+
return `\`${name}\``;
|
|
26
|
+
}
|
|
27
|
+
getPlaceholder(_index) {
|
|
28
|
+
return '?';
|
|
29
|
+
}
|
|
30
|
+
fieldToSqlType(field) {
|
|
31
|
+
return MYSQL_TYPE_MAP[field.type] || 'TEXT';
|
|
32
|
+
}
|
|
33
|
+
getIdColumnType() {
|
|
34
|
+
return 'VARCHAR(36)';
|
|
35
|
+
}
|
|
36
|
+
getTableListQuery() {
|
|
37
|
+
return "SELECT table_name as name FROM information_schema.tables WHERE table_schema = DATABASE()";
|
|
38
|
+
}
|
|
39
|
+
// --- Hooks ---
|
|
40
|
+
supportsIfNotExists() { return true; }
|
|
41
|
+
supportsReturning() { return false; }
|
|
42
|
+
// MySQL uses TINYINT(1) for boolean: 1 = true, 0 = false
|
|
43
|
+
serializeBoolean(v) { return v ? 1 : 0; }
|
|
44
|
+
deserializeBoolean(v) {
|
|
45
|
+
return v === 1 || v === true || v === '1';
|
|
46
|
+
}
|
|
47
|
+
// --- Connection ---
|
|
48
|
+
async doConnect(config) {
|
|
49
|
+
try {
|
|
50
|
+
const mysql2 = await import(/* webpackIgnore: true */ 'mysql2/promise');
|
|
51
|
+
const createPool = mysql2.default?.createPool || mysql2.createPool;
|
|
52
|
+
this.pool = createPool({
|
|
53
|
+
uri: config.uri,
|
|
54
|
+
connectionLimit: config.poolSize ?? 10,
|
|
55
|
+
waitForConnections: true,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
throw new Error(`MySQL driver not found. Install it: npm install mysql2\n` +
|
|
60
|
+
`Original error: ${e instanceof Error ? e.message : String(e)}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async doDisconnect() {
|
|
64
|
+
if (this.pool) {
|
|
65
|
+
await this.pool.end();
|
|
66
|
+
this.pool = null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async doTestConnection() {
|
|
70
|
+
if (!this.pool)
|
|
71
|
+
return false;
|
|
72
|
+
const conn = await this.pool.getConnection();
|
|
73
|
+
try {
|
|
74
|
+
await conn.query('SELECT 1');
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
conn.release();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// --- Query execution ---
|
|
82
|
+
async executeQuery(sql, params) {
|
|
83
|
+
if (!this.pool)
|
|
84
|
+
throw new Error('MySQL not connected. Call connect() first.');
|
|
85
|
+
const [rows] = await this.pool.execute(sql, params);
|
|
86
|
+
return rows;
|
|
87
|
+
}
|
|
88
|
+
async executeRun(sql, params) {
|
|
89
|
+
if (!this.pool)
|
|
90
|
+
throw new Error('MySQL not connected. Call connect() first.');
|
|
91
|
+
const [result] = await this.pool.execute(sql, params);
|
|
92
|
+
return { changes: result.affectedRows ?? 0 };
|
|
93
|
+
}
|
|
94
|
+
getDialectLabel() { return 'MySQL'; }
|
|
95
|
+
}
|
|
96
|
+
// ============================================================
|
|
97
|
+
// Factory export
|
|
98
|
+
// ============================================================
|
|
99
|
+
export function createDialect() {
|
|
100
|
+
return new MySQLDialect();
|
|
101
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Oracle Database Dialect — extends AbstractSqlDialect
|
|
2
|
+
// Equivalent to org.hibernate.dialect.OracleDialect (Hibernate ORM 6.4)
|
|
3
|
+
// Driver: npm install oracledb
|
|
4
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
5
|
+
import { AbstractSqlDialect } from './abstract-sql.dialect.js';
|
|
6
|
+
// ============================================================
|
|
7
|
+
// Type Mapping — DAL FieldType → Oracle column type
|
|
8
|
+
// ============================================================
|
|
9
|
+
const ORACLE_TYPE_MAP = {
|
|
10
|
+
string: 'VARCHAR2(4000)',
|
|
11
|
+
number: 'NUMBER',
|
|
12
|
+
boolean: 'NUMBER(1)',
|
|
13
|
+
date: 'TIMESTAMP',
|
|
14
|
+
json: 'CLOB',
|
|
15
|
+
array: 'CLOB',
|
|
16
|
+
};
|
|
17
|
+
// ============================================================
|
|
18
|
+
// OracleDialect
|
|
19
|
+
// ============================================================
|
|
20
|
+
class OracleDialect extends AbstractSqlDialect {
|
|
21
|
+
dialectType = 'oracle';
|
|
22
|
+
pool = null;
|
|
23
|
+
oracledb = null;
|
|
24
|
+
// --- Abstract implementations ---
|
|
25
|
+
quoteIdentifier(name) {
|
|
26
|
+
return `"${name}"`;
|
|
27
|
+
}
|
|
28
|
+
// Oracle uses :1, :2, ... bind variables
|
|
29
|
+
getPlaceholder(index) {
|
|
30
|
+
return `:${index}`;
|
|
31
|
+
}
|
|
32
|
+
fieldToSqlType(field) {
|
|
33
|
+
return ORACLE_TYPE_MAP[field.type] || 'VARCHAR2(4000)';
|
|
34
|
+
}
|
|
35
|
+
getIdColumnType() {
|
|
36
|
+
return 'VARCHAR2(36)';
|
|
37
|
+
}
|
|
38
|
+
getTableListQuery() {
|
|
39
|
+
return "SELECT table_name as name FROM user_tables";
|
|
40
|
+
}
|
|
41
|
+
// --- Hooks ---
|
|
42
|
+
// Oracle prior to 23c doesn't support IF NOT EXISTS
|
|
43
|
+
supportsIfNotExists() { return false; }
|
|
44
|
+
supportsReturning() { return false; }
|
|
45
|
+
// Oracle NUMBER(1): 1 = true, 0 = false
|
|
46
|
+
serializeBoolean(v) { return v ? 1 : 0; }
|
|
47
|
+
deserializeBoolean(v) {
|
|
48
|
+
return v === 1 || v === true || v === '1';
|
|
49
|
+
}
|
|
50
|
+
/** Oracle LIKE is case-sensitive — use UPPER() for case-insensitive search */
|
|
51
|
+
buildRegexCondition(col, flags) {
|
|
52
|
+
if (flags?.includes('i')) {
|
|
53
|
+
return `UPPER(${col}) LIKE UPPER(${this.nextPlaceholder()})`;
|
|
54
|
+
}
|
|
55
|
+
return `${col} LIKE ${this.nextPlaceholder()}`;
|
|
56
|
+
}
|
|
57
|
+
// Oracle uses OFFSET n ROWS FETCH FIRST m ROWS ONLY (12c+)
|
|
58
|
+
buildLimitOffset(options) {
|
|
59
|
+
if (!options?.limit && !options?.skip)
|
|
60
|
+
return '';
|
|
61
|
+
const offset = options.skip ?? 0;
|
|
62
|
+
const limit = options.limit;
|
|
63
|
+
let sql = ` OFFSET ${offset} ROWS`;
|
|
64
|
+
if (limit)
|
|
65
|
+
sql += ` FETCH FIRST ${limit} ROWS ONLY`;
|
|
66
|
+
return sql;
|
|
67
|
+
}
|
|
68
|
+
// Oracle: use PL/SQL block to check existence before CREATE TABLE
|
|
69
|
+
getCreateTablePrefix(tableName) {
|
|
70
|
+
const q = this.quoteIdentifier(tableName);
|
|
71
|
+
return `CREATE TABLE ${q}`;
|
|
72
|
+
}
|
|
73
|
+
getCreateIndexPrefix(indexName, unique) {
|
|
74
|
+
const u = unique ? 'UNIQUE ' : '';
|
|
75
|
+
return `CREATE ${u}INDEX ${this.quoteIdentifier(indexName)}`;
|
|
76
|
+
}
|
|
77
|
+
// --- Connection ---
|
|
78
|
+
async doConnect(config) {
|
|
79
|
+
try {
|
|
80
|
+
const oracledb = await import(/* webpackIgnore: true */ 'oracledb');
|
|
81
|
+
this.oracledb = oracledb.default || oracledb;
|
|
82
|
+
this.oracledb.outFormat = this.oracledb.OUT_FORMAT_OBJECT;
|
|
83
|
+
this.oracledb.autoCommit = true;
|
|
84
|
+
this.pool = await this.oracledb.createPool({
|
|
85
|
+
connectString: config.uri,
|
|
86
|
+
poolMax: config.poolSize ?? 10,
|
|
87
|
+
poolMin: 2,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
throw new Error(`Oracle driver not found. Install it: npm install oracledb\n` +
|
|
92
|
+
`Original error: ${e instanceof Error ? e.message : String(e)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async doDisconnect() {
|
|
96
|
+
if (this.pool) {
|
|
97
|
+
await this.pool.close(0);
|
|
98
|
+
this.pool = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async doTestConnection() {
|
|
102
|
+
if (!this.pool)
|
|
103
|
+
return false;
|
|
104
|
+
const conn = await this.pool.getConnection();
|
|
105
|
+
try {
|
|
106
|
+
await conn.execute('SELECT 1 FROM DUAL');
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
await conn.close();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// --- Query execution ---
|
|
114
|
+
async executeQuery(sql, params) {
|
|
115
|
+
if (!this.pool)
|
|
116
|
+
throw new Error('Oracle not connected. Call connect() first.');
|
|
117
|
+
const conn = await this.pool.getConnection();
|
|
118
|
+
try {
|
|
119
|
+
const result = await conn.execute(sql, params);
|
|
120
|
+
return result.rows ?? [];
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await conn.close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async executeRun(sql, params) {
|
|
127
|
+
if (!this.pool)
|
|
128
|
+
throw new Error('Oracle not connected. Call connect() first.');
|
|
129
|
+
const conn = await this.pool.getConnection();
|
|
130
|
+
try {
|
|
131
|
+
const result = await conn.execute(sql, params);
|
|
132
|
+
return { changes: result.rowsAffected ?? 0 };
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
await conn.close();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Override initSchema to handle Oracle's lack of IF NOT EXISTS
|
|
139
|
+
async initSchema(schemas) {
|
|
140
|
+
this.schemas = schemas;
|
|
141
|
+
const strategy = this.config?.schemaStrategy ?? 'none';
|
|
142
|
+
this.log('INIT_SCHEMA', `strategy=${strategy}`, { entities: schemas.map(s => s.name) });
|
|
143
|
+
if (strategy === 'none')
|
|
144
|
+
return;
|
|
145
|
+
if (strategy === 'validate') {
|
|
146
|
+
for (const schema of schemas) {
|
|
147
|
+
const exists = await this.tableExists(schema.collection);
|
|
148
|
+
if (!exists) {
|
|
149
|
+
throw new Error(`Schema validation failed: table "${schema.collection}" does not exist ` +
|
|
150
|
+
`(entity: ${schema.name}). Set schemaStrategy to "update" or "create".`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// For 'update' strategy: create tables only if they don't exist
|
|
156
|
+
for (const schema of schemas) {
|
|
157
|
+
const exists = await this.tableExists(schema.collection);
|
|
158
|
+
if (!exists) {
|
|
159
|
+
const createSql = this.generateCreateTable(schema);
|
|
160
|
+
this.log('DDL', schema.collection, createSql);
|
|
161
|
+
await this.executeRun(createSql, []);
|
|
162
|
+
}
|
|
163
|
+
// Indexes: check existence before creating
|
|
164
|
+
const indexStatements = this.generateIndexes(schema);
|
|
165
|
+
for (const stmt of indexStatements) {
|
|
166
|
+
try {
|
|
167
|
+
await this.executeRun(stmt, []);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Index may already exist — ignore
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Junction tables
|
|
175
|
+
for (const schema of schemas) {
|
|
176
|
+
for (const [, rel] of Object.entries(schema.relations)) {
|
|
177
|
+
if (rel.type === 'many-to-many' && rel.through) {
|
|
178
|
+
const exists = await this.tableExists(rel.through);
|
|
179
|
+
if (exists)
|
|
180
|
+
continue;
|
|
181
|
+
const targetSchema = schemas.find(s => s.name === rel.target);
|
|
182
|
+
if (!targetSchema)
|
|
183
|
+
continue;
|
|
184
|
+
const sourceKey = `${schema.name.toLowerCase()}Id`;
|
|
185
|
+
const targetKey = `${rel.target.toLowerCase()}Id`;
|
|
186
|
+
const q = (n) => this.quoteIdentifier(n);
|
|
187
|
+
const idType = this.getIdColumnType();
|
|
188
|
+
const ddl = `CREATE TABLE ${q(rel.through)} (
|
|
189
|
+
${q(sourceKey)} ${idType} NOT NULL,
|
|
190
|
+
${q(targetKey)} ${idType} NOT NULL,
|
|
191
|
+
PRIMARY KEY (${q(sourceKey)}, ${q(targetKey)})
|
|
192
|
+
)`;
|
|
193
|
+
this.log('DDL_JUNCTION', rel.through, ddl);
|
|
194
|
+
await this.executeRun(ddl, []);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
getDialectLabel() { return 'Oracle'; }
|
|
200
|
+
}
|
|
201
|
+
// ============================================================
|
|
202
|
+
// Factory export
|
|
203
|
+
// ============================================================
|
|
204
|
+
export function createDialect() {
|
|
205
|
+
return new OracleDialect();
|
|
206
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IDialect, DialectType, ConnectionConfig, FieldDef } from '../core/types.js';
|
|
2
|
+
import { AbstractSqlDialect } from './abstract-sql.dialect.js';
|
|
3
|
+
export declare class PostgresDialect extends AbstractSqlDialect {
|
|
4
|
+
readonly dialectType: DialectType;
|
|
5
|
+
protected pool: unknown;
|
|
6
|
+
quoteIdentifier(name: string): string;
|
|
7
|
+
getPlaceholder(index: number): string;
|
|
8
|
+
fieldToSqlType(field: FieldDef): string;
|
|
9
|
+
getIdColumnType(): string;
|
|
10
|
+
getTableListQuery(): string;
|
|
11
|
+
protected supportsIfNotExists(): boolean;
|
|
12
|
+
protected supportsReturning(): boolean;
|
|
13
|
+
protected serializeBoolean(v: boolean): unknown;
|
|
14
|
+
protected deserializeBoolean(v: unknown): boolean;
|
|
15
|
+
/** PostgreSQL LIKE is case-sensitive — use ILIKE when flags contain 'i' */
|
|
16
|
+
protected buildRegexCondition(col: string, flags?: string): string;
|
|
17
|
+
doConnect(config: ConnectionConfig): Promise<void>;
|
|
18
|
+
doDisconnect(): Promise<void>;
|
|
19
|
+
doTestConnection(): Promise<boolean>;
|
|
20
|
+
executeQuery<T>(sql: string, params: unknown[]): Promise<T[]>;
|
|
21
|
+
executeRun(sql: string, params: unknown[]): Promise<{
|
|
22
|
+
changes: number;
|
|
23
|
+
}>;
|
|
24
|
+
protected getDialectLabel(): string;
|
|
25
|
+
}
|
|
26
|
+
export declare function createDialect(): IDialect;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// PostgreSQL Dialect — extends AbstractSqlDialect
|
|
2
|
+
// Equivalent to org.hibernate.dialect.PostgreSQLDialect (Hibernate ORM 6.4)
|
|
3
|
+
// Driver: npm install pg
|
|
4
|
+
// Author: Dr Hamid MADANI drmdh@msn.com
|
|
5
|
+
import { AbstractSqlDialect } from './abstract-sql.dialect.js';
|
|
6
|
+
// ============================================================
|
|
7
|
+
// Type Mapping — DAL FieldType → PostgreSQL column type
|
|
8
|
+
// ============================================================
|
|
9
|
+
const PG_TYPE_MAP = {
|
|
10
|
+
string: 'TEXT',
|
|
11
|
+
number: 'DOUBLE PRECISION',
|
|
12
|
+
boolean: 'BOOLEAN',
|
|
13
|
+
date: 'TIMESTAMPTZ',
|
|
14
|
+
json: 'JSONB',
|
|
15
|
+
array: 'JSONB',
|
|
16
|
+
};
|
|
17
|
+
// ============================================================
|
|
18
|
+
// PostgresDialect
|
|
19
|
+
// ============================================================
|
|
20
|
+
export class PostgresDialect extends AbstractSqlDialect {
|
|
21
|
+
dialectType = 'postgres';
|
|
22
|
+
pool = null;
|
|
23
|
+
// --- Abstract implementations ---
|
|
24
|
+
quoteIdentifier(name) {
|
|
25
|
+
return `"${name}"`;
|
|
26
|
+
}
|
|
27
|
+
getPlaceholder(index) {
|
|
28
|
+
return `$${index}`;
|
|
29
|
+
}
|
|
30
|
+
fieldToSqlType(field) {
|
|
31
|
+
return PG_TYPE_MAP[field.type] || 'TEXT';
|
|
32
|
+
}
|
|
33
|
+
getIdColumnType() {
|
|
34
|
+
return 'TEXT';
|
|
35
|
+
}
|
|
36
|
+
getTableListQuery() {
|
|
37
|
+
return "SELECT tablename as name FROM pg_tables WHERE schemaname = 'public'";
|
|
38
|
+
}
|
|
39
|
+
// --- Hooks ---
|
|
40
|
+
supportsIfNotExists() { return true; }
|
|
41
|
+
supportsReturning() { return true; }
|
|
42
|
+
serializeBoolean(v) { return v; }
|
|
43
|
+
deserializeBoolean(v) { return v === true || v === 't' || v === 'true'; }
|
|
44
|
+
/** PostgreSQL LIKE is case-sensitive — use ILIKE when flags contain 'i' */
|
|
45
|
+
buildRegexCondition(col, flags) {
|
|
46
|
+
if (flags?.includes('i')) {
|
|
47
|
+
return `${col} ILIKE ${this.nextPlaceholder()}`;
|
|
48
|
+
}
|
|
49
|
+
return `${col} LIKE ${this.nextPlaceholder()}`;
|
|
50
|
+
}
|
|
51
|
+
// --- Connection ---
|
|
52
|
+
async doConnect(config) {
|
|
53
|
+
try {
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
55
|
+
const pg = await import(/* webpackIgnore: true */ 'pg');
|
|
56
|
+
const Pool = pg.default?.Pool || pg.Pool;
|
|
57
|
+
this.pool = new Pool({
|
|
58
|
+
connectionString: config.uri,
|
|
59
|
+
max: config.poolSize ?? 10,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
throw new Error(`PostgreSQL driver not found. Install it: npm install pg\n` +
|
|
64
|
+
`Original error: ${e instanceof Error ? e.message : String(e)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async doDisconnect() {
|
|
68
|
+
if (this.pool) {
|
|
69
|
+
await this.pool.end();
|
|
70
|
+
this.pool = null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async doTestConnection() {
|
|
74
|
+
if (!this.pool)
|
|
75
|
+
return false;
|
|
76
|
+
const client = await this.pool.connect();
|
|
77
|
+
try {
|
|
78
|
+
await client.query('SELECT 1');
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
client.release();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// --- Query execution ---
|
|
86
|
+
async executeQuery(sql, params) {
|
|
87
|
+
if (!this.pool)
|
|
88
|
+
throw new Error('PostgreSQL not connected. Call connect() first.');
|
|
89
|
+
const result = await this.pool.query(sql, params);
|
|
90
|
+
return result.rows;
|
|
91
|
+
}
|
|
92
|
+
async executeRun(sql, params) {
|
|
93
|
+
if (!this.pool)
|
|
94
|
+
throw new Error('PostgreSQL not connected. Call connect() first.');
|
|
95
|
+
const result = await this.pool.query(sql, params);
|
|
96
|
+
return { changes: result.rowCount ?? 0 };
|
|
97
|
+
}
|
|
98
|
+
getDialectLabel() { return 'PostgreSQL'; }
|
|
99
|
+
}
|
|
100
|
+
// ============================================================
|
|
101
|
+
// Factory export
|
|
102
|
+
// ============================================================
|
|
103
|
+
export function createDialect() {
|
|
104
|
+
return new PostgresDialect();
|
|
105
|
+
}
|