@db-bridge/postgresql 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 +436 -0
- package/dist/index.d.ts +91 -0
- package/dist/index.js +619 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Berke Erdoğan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# @db-bridge/postgresql
|
|
2
|
+
|
|
3
|
+
PostgreSQL adapter for DB Bridge - A comprehensive database management library.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @db-bridge/postgresql @db-bridge/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Full PostgreSQL support (9.5+)
|
|
14
|
+
- Connection pooling with pg
|
|
15
|
+
- Prepared statements
|
|
16
|
+
- Advanced transaction management with savepoints and isolation levels
|
|
17
|
+
- JSONB and array support
|
|
18
|
+
- TypeScript support
|
|
19
|
+
- Query builder integration
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { PostgreSQLAdapter } from '@db-bridge/postgresql';
|
|
25
|
+
|
|
26
|
+
const adapter = new PostgreSQLAdapter();
|
|
27
|
+
|
|
28
|
+
// Connect
|
|
29
|
+
await adapter.connect({
|
|
30
|
+
host: 'localhost',
|
|
31
|
+
port: 5432,
|
|
32
|
+
user: 'postgres',
|
|
33
|
+
password: 'password',
|
|
34
|
+
database: 'myapp',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Simple query
|
|
38
|
+
const users = await adapter.query('SELECT * FROM users WHERE active = $1', [true]);
|
|
39
|
+
|
|
40
|
+
// Query builder
|
|
41
|
+
const qb = adapter.createQueryBuilder();
|
|
42
|
+
const results = await qb
|
|
43
|
+
.select('*')
|
|
44
|
+
.from('users')
|
|
45
|
+
.where('age', '>', 18)
|
|
46
|
+
.orderBy('created_at', 'DESC')
|
|
47
|
+
.limit(10)
|
|
48
|
+
.execute();
|
|
49
|
+
|
|
50
|
+
// Disconnect
|
|
51
|
+
await adapter.disconnect();
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
### Connection Options
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
interface PostgreSQLConnectionConfig {
|
|
60
|
+
host: string;
|
|
61
|
+
port?: number;
|
|
62
|
+
user: string;
|
|
63
|
+
password: string;
|
|
64
|
+
database: string;
|
|
65
|
+
ssl?: boolean | TlsOptions;
|
|
66
|
+
connectionTimeoutMillis?: number;
|
|
67
|
+
idle_in_transaction_session_timeout?: number;
|
|
68
|
+
statement_timeout?: number;
|
|
69
|
+
query_timeout?: number;
|
|
70
|
+
application_name?: string;
|
|
71
|
+
max?: number; // Pool size
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Adapter Options
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
const adapter = new PostgreSQLAdapter({
|
|
79
|
+
// Logger instance (optional)
|
|
80
|
+
logger: console,
|
|
81
|
+
|
|
82
|
+
// Parse PostgreSQL types automatically
|
|
83
|
+
parseTypes: true,
|
|
84
|
+
|
|
85
|
+
// Retry options
|
|
86
|
+
retryOptions: {
|
|
87
|
+
maxRetries: 3,
|
|
88
|
+
retryDelay: 1000,
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// pg specific options
|
|
92
|
+
pgOptions: {
|
|
93
|
+
ssl: {
|
|
94
|
+
rejectUnauthorized: false,
|
|
95
|
+
ca: fs.readFileSync('server-ca.pem'),
|
|
96
|
+
},
|
|
97
|
+
statement_timeout: 30000,
|
|
98
|
+
query_timeout: 60000,
|
|
99
|
+
connectionTimeoutMillis: 10000,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Usage
|
|
105
|
+
|
|
106
|
+
### Basic Queries
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Parameterized query with $1, $2 placeholders
|
|
110
|
+
const result = await adapter.query('SELECT * FROM products WHERE price > $1 AND category = $2', [
|
|
111
|
+
100,
|
|
112
|
+
'electronics',
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
// RETURNING clause
|
|
116
|
+
const inserted = await adapter.query(
|
|
117
|
+
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
|
|
118
|
+
['John Doe', 'john@example.com'],
|
|
119
|
+
);
|
|
120
|
+
console.log('Inserted user:', inserted.rows[0]);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Query Builder
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const qb = adapter.createQueryBuilder();
|
|
127
|
+
|
|
128
|
+
// SELECT with joins
|
|
129
|
+
const query = qb
|
|
130
|
+
.select('u.*', 'COUNT(o.id) as order_count')
|
|
131
|
+
.from('users', 'u')
|
|
132
|
+
.leftJoin('orders o', 'o.user_id = u.id')
|
|
133
|
+
.where('u.active', true)
|
|
134
|
+
.groupBy('u.id')
|
|
135
|
+
.having('COUNT(o.id) > ?', [5])
|
|
136
|
+
.orderBy('order_count', 'DESC');
|
|
137
|
+
|
|
138
|
+
const results = await query.get();
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Transactions
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
// Basic transaction
|
|
145
|
+
const transaction = await adapter.beginTransaction();
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
await adapter.execute('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [100, 1], {
|
|
149
|
+
transaction,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await adapter.execute('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [100, 2], {
|
|
153
|
+
transaction,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await transaction.commit();
|
|
157
|
+
} catch (error) {
|
|
158
|
+
await transaction.rollback();
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Transaction with isolation level
|
|
163
|
+
const tx = await adapter.beginTransaction({
|
|
164
|
+
isolationLevel: IsolationLevel.SERIALIZABLE,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Savepoints
|
|
168
|
+
await tx.savepoint('before_update');
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
await riskyOperation(tx);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
await tx.rollbackToSavepoint('before_update');
|
|
174
|
+
// Continue transaction
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
await tx.commit();
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### PostgreSQL-Specific Features
|
|
181
|
+
|
|
182
|
+
#### JSONB Support
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Insert JSONB data
|
|
186
|
+
await adapter.execute('INSERT INTO products (name, attributes) VALUES ($1, $2)', [
|
|
187
|
+
'Laptop',
|
|
188
|
+
{ brand: 'Dell', specs: { ram: '16GB', ssd: '512GB' } },
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
// Query JSONB fields
|
|
192
|
+
const products = await adapter.query("SELECT * FROM products WHERE attributes->>'brand' = $1", [
|
|
193
|
+
'Dell',
|
|
194
|
+
]);
|
|
195
|
+
|
|
196
|
+
// JSONB operators
|
|
197
|
+
const results = await adapter.query('SELECT * FROM products WHERE attributes @> $1', [
|
|
198
|
+
{ brand: 'Dell' },
|
|
199
|
+
]);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### Array Types
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// Insert arrays
|
|
206
|
+
await adapter.execute('INSERT INTO posts (title, tags) VALUES ($1, $2)', [
|
|
207
|
+
'PostgreSQL Arrays',
|
|
208
|
+
['database', 'postgresql', 'arrays'],
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
// Query arrays
|
|
212
|
+
const posts = await adapter.query('SELECT * FROM posts WHERE tags @> $1', [['postgresql']]);
|
|
213
|
+
|
|
214
|
+
// Array functions
|
|
215
|
+
const tagged = await adapter.query('SELECT * FROM posts WHERE $1 = ANY(tags)', ['database']);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### Full-Text Search
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Create text search vector
|
|
222
|
+
await adapter.execute(`
|
|
223
|
+
ALTER TABLE articles
|
|
224
|
+
ADD COLUMN search_vector tsvector
|
|
225
|
+
GENERATED ALWAYS AS (
|
|
226
|
+
to_tsvector('english', title || ' ' || content)
|
|
227
|
+
) STORED
|
|
228
|
+
`);
|
|
229
|
+
|
|
230
|
+
// Full-text search
|
|
231
|
+
const results = await adapter.query(
|
|
232
|
+
"SELECT * FROM articles WHERE search_vector @@ plainto_tsquery('english', $1)",
|
|
233
|
+
['database management'],
|
|
234
|
+
);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### Window Functions
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
const analytics = await adapter.query(
|
|
241
|
+
`
|
|
242
|
+
SELECT
|
|
243
|
+
user_id,
|
|
244
|
+
created_at,
|
|
245
|
+
amount,
|
|
246
|
+
SUM(amount) OVER (PARTITION BY user_id ORDER BY created_at) as running_total,
|
|
247
|
+
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY amount DESC) as rank
|
|
248
|
+
FROM transactions
|
|
249
|
+
WHERE created_at > $1
|
|
250
|
+
`,
|
|
251
|
+
['2024-01-01'],
|
|
252
|
+
);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### UPSERT (INSERT ... ON CONFLICT)
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
await adapter.execute(
|
|
259
|
+
`
|
|
260
|
+
INSERT INTO user_settings (user_id, key, value)
|
|
261
|
+
VALUES ($1, $2, $3)
|
|
262
|
+
ON CONFLICT (user_id, key)
|
|
263
|
+
DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()
|
|
264
|
+
`,
|
|
265
|
+
[123, 'theme', 'dark'],
|
|
266
|
+
);
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Prepared Statements
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
// Named prepared statement
|
|
273
|
+
const stmt = await adapter.prepare(
|
|
274
|
+
'SELECT * FROM orders WHERE user_id = $1 AND status = $2',
|
|
275
|
+
'getUserOrders',
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
// Execute multiple times
|
|
279
|
+
const pendingOrders = await stmt.execute([123, 'pending']);
|
|
280
|
+
const completedOrders = await stmt.execute([123, 'completed']);
|
|
281
|
+
|
|
282
|
+
// Release when done
|
|
283
|
+
await stmt.release();
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Connection Pool Management
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
// Get pool statistics
|
|
290
|
+
const stats = adapter.getPoolStats();
|
|
291
|
+
console.log('Total connections:', stats.total);
|
|
292
|
+
console.log('Idle connections:', stats.idle);
|
|
293
|
+
console.log('Active connections:', stats.active);
|
|
294
|
+
console.log('Waiting clients:', stats.waiting);
|
|
295
|
+
|
|
296
|
+
// Test connection
|
|
297
|
+
const isAlive = await adapter.ping();
|
|
298
|
+
|
|
299
|
+
// Listen to pool events
|
|
300
|
+
adapter.on('connect', (client) => {
|
|
301
|
+
console.log('New client connected');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
adapter.on('error', (err, client) => {
|
|
305
|
+
console.error('Pool error:', err);
|
|
306
|
+
});
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### COPY Operations
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
// COPY FROM for bulk insert
|
|
313
|
+
const copyStream = adapter.copyFrom('COPY users (name, email) FROM STDIN WITH (FORMAT csv)');
|
|
314
|
+
|
|
315
|
+
copyStream.write('John Doe,john@example.com\n');
|
|
316
|
+
copyStream.write('Jane Smith,jane@example.com\n');
|
|
317
|
+
copyStream.end();
|
|
318
|
+
|
|
319
|
+
// COPY TO for export
|
|
320
|
+
const outputStream = adapter.copyTo('COPY users TO STDOUT WITH (FORMAT csv, HEADER true)');
|
|
321
|
+
|
|
322
|
+
outputStream.pipe(fs.createWriteStream('users.csv'));
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Listen/Notify
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// Listen for notifications
|
|
329
|
+
await adapter.execute('LISTEN user_updates');
|
|
330
|
+
|
|
331
|
+
adapter.on('notification', (msg) => {
|
|
332
|
+
console.log('Notification received:', msg.channel, msg.payload);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Send notification
|
|
336
|
+
await adapter.execute('NOTIFY user_updates, $1', [
|
|
337
|
+
JSON.stringify({ userId: 123, action: 'updated' }),
|
|
338
|
+
]);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Advanced Features
|
|
342
|
+
|
|
343
|
+
### Cursors
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
// Server-side cursor for large result sets
|
|
347
|
+
const cursor = await adapter.cursor('SELECT * FROM large_table WHERE created_at > $1', [
|
|
348
|
+
'2024-01-01',
|
|
349
|
+
]);
|
|
350
|
+
|
|
351
|
+
let rows;
|
|
352
|
+
while ((rows = await cursor.read(100)).length > 0) {
|
|
353
|
+
// Process batch of 100 rows
|
|
354
|
+
await processBatch(rows);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
await cursor.close();
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Type Parsing
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// Custom type parsing
|
|
364
|
+
adapter.setTypeParser(1082, (val) => {
|
|
365
|
+
// Parse date type as moment
|
|
366
|
+
return moment(val);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// Built-in parsing for common types
|
|
370
|
+
const result = await adapter.query('SELECT NOW() as time, 123::int8 as bigint');
|
|
371
|
+
console.log(typeof result.rows[0].bigint); // 'bigint' in Node.js 10.4+
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Error Handling
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
import { ConnectionError, QueryError } from '@db-bridge/core';
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
await adapter.query('SELECT * FROM users');
|
|
381
|
+
} catch (error) {
|
|
382
|
+
if (error instanceof QueryError) {
|
|
383
|
+
switch (error.code) {
|
|
384
|
+
case '23505': // unique_violation
|
|
385
|
+
console.error('Duplicate key error');
|
|
386
|
+
break;
|
|
387
|
+
case '23503': // foreign_key_violation
|
|
388
|
+
console.error('Foreign key constraint error');
|
|
389
|
+
break;
|
|
390
|
+
case '42P01': // undefined_table
|
|
391
|
+
console.error('Table does not exist');
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Best Practices
|
|
399
|
+
|
|
400
|
+
1. **Use Parameterized Queries**: Always use $1, $2 placeholders
|
|
401
|
+
2. **Set Statement Timeout**: Prevent long-running queries
|
|
402
|
+
3. **Use Connection Pooling**: Configure appropriate pool size
|
|
403
|
+
4. **Handle JSON Types**: Use native JavaScript objects with JSONB
|
|
404
|
+
5. **Use Transactions**: For data consistency
|
|
405
|
+
6. **Monitor Pool Health**: Track connection pool statistics
|
|
406
|
+
|
|
407
|
+
## TypeScript Support
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
interface User {
|
|
411
|
+
id: number;
|
|
412
|
+
name: string;
|
|
413
|
+
email: string;
|
|
414
|
+
metadata: {
|
|
415
|
+
role: string;
|
|
416
|
+
department?: string;
|
|
417
|
+
};
|
|
418
|
+
tags: string[];
|
|
419
|
+
created_at: Date;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Type-safe queries
|
|
423
|
+
const users = await adapter.query<User>('SELECT * FROM users WHERE id = $1', [123]);
|
|
424
|
+
|
|
425
|
+
// Type-safe query builder
|
|
426
|
+
const user = await adapter
|
|
427
|
+
.createQueryBuilder<User>()
|
|
428
|
+
.select('*')
|
|
429
|
+
.from('users')
|
|
430
|
+
.where('metadata->>"role"', '=', 'admin')
|
|
431
|
+
.first();
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## License
|
|
435
|
+
|
|
436
|
+
MIT © Berke Erdoğan
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { BaseQueryBuilder, BaseAdapter, BaseAdapterOptions, ConnectionConfig, QueryParams, QueryOptions, QueryResult, TransactionOptions, Transaction, PreparedStatement, PoolStats } from '@db-bridge/core';
|
|
2
|
+
export { ConnectionConfig, DatabaseAdapter, PreparedStatement, QueryBuilder, QueryOptions, QueryResult, Transaction } from '@db-bridge/core';
|
|
3
|
+
import { PoolConfig, PoolClient } from 'pg';
|
|
4
|
+
|
|
5
|
+
declare class PostgreSQLQueryBuilder<T = unknown> extends BaseQueryBuilder<T> {
|
|
6
|
+
protected buildSelectSQL(): {
|
|
7
|
+
sql: string;
|
|
8
|
+
bindings: unknown[];
|
|
9
|
+
};
|
|
10
|
+
protected buildInsertSQL(): {
|
|
11
|
+
sql: string;
|
|
12
|
+
bindings: unknown[];
|
|
13
|
+
};
|
|
14
|
+
protected buildUpdateSQL(): {
|
|
15
|
+
sql: string;
|
|
16
|
+
bindings: unknown[];
|
|
17
|
+
};
|
|
18
|
+
protected buildDeleteSQL(): {
|
|
19
|
+
sql: string;
|
|
20
|
+
bindings: unknown[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface PostgreSQLAdapterOptions extends BaseAdapterOptions {
|
|
25
|
+
pgOptions?: PoolConfig;
|
|
26
|
+
parseTypes?: boolean;
|
|
27
|
+
}
|
|
28
|
+
declare class PostgreSQLAdapter extends BaseAdapter {
|
|
29
|
+
readonly name = "PostgreSQL";
|
|
30
|
+
readonly version = "2.0.0";
|
|
31
|
+
private pool?;
|
|
32
|
+
private readonly pgOptions?;
|
|
33
|
+
private readonly parseTypes;
|
|
34
|
+
private preparedStatements;
|
|
35
|
+
constructor(options?: PostgreSQLAdapterOptions);
|
|
36
|
+
protected doConnect(config: ConnectionConfig): Promise<void>;
|
|
37
|
+
protected doDisconnect(): Promise<void>;
|
|
38
|
+
protected doQuery<T = unknown>(sql: string, params?: QueryParams, options?: QueryOptions): Promise<QueryResult<T>>;
|
|
39
|
+
beginTransaction(options?: TransactionOptions): Promise<Transaction>;
|
|
40
|
+
prepare<T = unknown>(sql: string, name?: string): Promise<PreparedStatement<T>>;
|
|
41
|
+
getPoolStats(): PoolStats;
|
|
42
|
+
ping(): Promise<boolean>;
|
|
43
|
+
escape(value: unknown): string;
|
|
44
|
+
escapeIdentifier(identifier: string): string;
|
|
45
|
+
createQueryBuilder<T = unknown>(): PostgreSQLQueryBuilder<T>;
|
|
46
|
+
private normalizeParams;
|
|
47
|
+
private configurePgTypes;
|
|
48
|
+
private getFieldType;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
declare class PostgreSQLTransaction implements Transaction {
|
|
52
|
+
private client;
|
|
53
|
+
private options?;
|
|
54
|
+
readonly id: string;
|
|
55
|
+
private _isActive;
|
|
56
|
+
private savepoints;
|
|
57
|
+
constructor(client: PoolClient, options?: TransactionOptions | undefined);
|
|
58
|
+
get isActive(): boolean;
|
|
59
|
+
begin(): Promise<void>;
|
|
60
|
+
commit(): Promise<void>;
|
|
61
|
+
rollback(): Promise<void>;
|
|
62
|
+
savepoint(name: string): Promise<void>;
|
|
63
|
+
releaseSavepoint(name: string): Promise<void>;
|
|
64
|
+
rollbackToSavepoint(name: string): Promise<void>;
|
|
65
|
+
getClient(): PoolClient;
|
|
66
|
+
query<T = unknown>(sql: string, params?: QueryParams, _options?: QueryOptions): Promise<QueryResult<T>>;
|
|
67
|
+
execute<T = unknown>(sql: string, params?: QueryParams, options?: QueryOptions): Promise<QueryResult<T>>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
declare class PostgreSQLPreparedStatement<T = unknown> implements PreparedStatement<T> {
|
|
71
|
+
private client;
|
|
72
|
+
private sql;
|
|
73
|
+
private name;
|
|
74
|
+
private released;
|
|
75
|
+
constructor(client: PoolClient, sql: string, name: string);
|
|
76
|
+
execute(params?: unknown[]): Promise<QueryResult<T>>;
|
|
77
|
+
release(): Promise<void>;
|
|
78
|
+
close(): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
declare class PostgreSQLConnectionPool {
|
|
82
|
+
private config;
|
|
83
|
+
private pool?;
|
|
84
|
+
constructor(config: PoolConfig);
|
|
85
|
+
initialize(): Promise<void>;
|
|
86
|
+
getClient(): Promise<PoolClient>;
|
|
87
|
+
end(): Promise<void>;
|
|
88
|
+
getStats(): PoolStats;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { PostgreSQLAdapter, type PostgreSQLAdapterOptions, PostgreSQLConnectionPool, PostgreSQLPreparedStatement, PostgreSQLQueryBuilder, PostgreSQLTransaction };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
import { QueryError, generateUUID, TransactionError, ConnectionError, BaseQueryBuilder, ValidationError, BaseAdapter } from '@db-bridge/core';
|
|
2
|
+
import { Pool, types } from 'pg';
|
|
3
|
+
|
|
4
|
+
// src/adapter/postgresql-adapter.ts
|
|
5
|
+
var PostgreSQLPreparedStatement = class {
|
|
6
|
+
constructor(client, sql, name) {
|
|
7
|
+
this.client = client;
|
|
8
|
+
this.sql = sql;
|
|
9
|
+
this.name = name;
|
|
10
|
+
}
|
|
11
|
+
released = false;
|
|
12
|
+
async execute(params) {
|
|
13
|
+
if (this.released) {
|
|
14
|
+
throw new QueryError("Prepared statement has been released");
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const result = await this.client.query({
|
|
18
|
+
text: this.sql,
|
|
19
|
+
name: this.name,
|
|
20
|
+
values: params || []
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
rows: result.rows,
|
|
24
|
+
rowCount: result.rowCount || 0,
|
|
25
|
+
affectedRows: result.rowCount || 0,
|
|
26
|
+
fields: result.fields?.map((field) => ({
|
|
27
|
+
name: field.name,
|
|
28
|
+
type: field.dataTypeID.toString(),
|
|
29
|
+
nullable: true,
|
|
30
|
+
primaryKey: false,
|
|
31
|
+
autoIncrement: false,
|
|
32
|
+
defaultValue: void 0
|
|
33
|
+
})),
|
|
34
|
+
command: result.command
|
|
35
|
+
};
|
|
36
|
+
} catch (error) {
|
|
37
|
+
throw new QueryError(
|
|
38
|
+
`Prepared statement execution failed: ${error.message}`,
|
|
39
|
+
this.sql,
|
|
40
|
+
params,
|
|
41
|
+
error
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async release() {
|
|
46
|
+
if (this.released) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
this.client.release();
|
|
51
|
+
this.released = true;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw new QueryError(
|
|
54
|
+
`Failed to release prepared statement: ${error.message}`,
|
|
55
|
+
this.sql,
|
|
56
|
+
void 0,
|
|
57
|
+
error
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Alias for release() - industry standard naming
|
|
63
|
+
*/
|
|
64
|
+
async close() {
|
|
65
|
+
return this.release();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var PostgreSQLTransaction = class {
|
|
69
|
+
constructor(client, options) {
|
|
70
|
+
this.client = client;
|
|
71
|
+
this.options = options;
|
|
72
|
+
this.id = generateUUID();
|
|
73
|
+
}
|
|
74
|
+
id;
|
|
75
|
+
_isActive = false;
|
|
76
|
+
savepoints = /* @__PURE__ */ new Set();
|
|
77
|
+
get isActive() {
|
|
78
|
+
return this._isActive;
|
|
79
|
+
}
|
|
80
|
+
async begin() {
|
|
81
|
+
if (this._isActive) {
|
|
82
|
+
throw new TransactionError("Transaction already active", this.id);
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const queries = [];
|
|
86
|
+
if (this.options?.isolationLevel) {
|
|
87
|
+
queries.push(`SET TRANSACTION ISOLATION LEVEL ${this.options.isolationLevel}`);
|
|
88
|
+
}
|
|
89
|
+
if (this.options?.readOnly) {
|
|
90
|
+
queries.push("SET TRANSACTION READ ONLY");
|
|
91
|
+
} else {
|
|
92
|
+
queries.push("SET TRANSACTION READ WRITE");
|
|
93
|
+
}
|
|
94
|
+
if (this.options?.deferrable) {
|
|
95
|
+
queries.push("SET TRANSACTION DEFERRABLE");
|
|
96
|
+
}
|
|
97
|
+
await this.client.query("BEGIN");
|
|
98
|
+
for (const query of queries) {
|
|
99
|
+
await this.client.query(query);
|
|
100
|
+
}
|
|
101
|
+
this._isActive = true;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
throw new TransactionError("Failed to begin transaction", this.id, error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async commit() {
|
|
107
|
+
if (!this._isActive) {
|
|
108
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
await this.client.query("COMMIT");
|
|
112
|
+
this._isActive = false;
|
|
113
|
+
this.savepoints.clear();
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new TransactionError("Failed to commit transaction", this.id, error);
|
|
116
|
+
} finally {
|
|
117
|
+
this.client.release();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async rollback() {
|
|
121
|
+
if (!this._isActive) {
|
|
122
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
await this.client.query("ROLLBACK");
|
|
126
|
+
this._isActive = false;
|
|
127
|
+
this.savepoints.clear();
|
|
128
|
+
} catch (error) {
|
|
129
|
+
throw new TransactionError("Failed to rollback transaction", this.id, error);
|
|
130
|
+
} finally {
|
|
131
|
+
this.client.release();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async savepoint(name) {
|
|
135
|
+
if (!this._isActive) {
|
|
136
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
137
|
+
}
|
|
138
|
+
if (this.savepoints.has(name)) {
|
|
139
|
+
throw new TransactionError(`Savepoint "${name}" already exists`, this.id);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const safeName = name.replaceAll(/\W/g, "_");
|
|
143
|
+
await this.client.query(`SAVEPOINT ${safeName}`);
|
|
144
|
+
this.savepoints.add(name);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
throw new TransactionError(`Failed to create savepoint "${name}"`, this.id, error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async releaseSavepoint(name) {
|
|
150
|
+
if (!this._isActive) {
|
|
151
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
152
|
+
}
|
|
153
|
+
if (!this.savepoints.has(name)) {
|
|
154
|
+
throw new TransactionError(`Savepoint "${name}" does not exist`, this.id);
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const safeName = name.replaceAll(/\W/g, "_");
|
|
158
|
+
await this.client.query(`RELEASE SAVEPOINT ${safeName}`);
|
|
159
|
+
this.savepoints.delete(name);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
throw new TransactionError(`Failed to release savepoint "${name}"`, this.id, error);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async rollbackToSavepoint(name) {
|
|
165
|
+
if (!this._isActive) {
|
|
166
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
167
|
+
}
|
|
168
|
+
if (!this.savepoints.has(name)) {
|
|
169
|
+
throw new TransactionError(`Savepoint "${name}" does not exist`, this.id);
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
const safeName = name.replaceAll(/\W/g, "_");
|
|
173
|
+
await this.client.query(`ROLLBACK TO SAVEPOINT ${safeName}`);
|
|
174
|
+
const savepointsArray = Array.from(this.savepoints);
|
|
175
|
+
const index = savepointsArray.indexOf(name);
|
|
176
|
+
if (index !== -1) {
|
|
177
|
+
for (let i = index + 1; i < savepointsArray.length; i++) {
|
|
178
|
+
this.savepoints.delete(savepointsArray[i]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
throw new TransactionError(
|
|
183
|
+
`Failed to rollback to savepoint "${name}"`,
|
|
184
|
+
this.id,
|
|
185
|
+
error
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
getClient() {
|
|
190
|
+
return this.client;
|
|
191
|
+
}
|
|
192
|
+
async query(sql, params, _options) {
|
|
193
|
+
if (!this._isActive) {
|
|
194
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const queryParams = Array.isArray(params) ? params : params ? Object.values(params) : [];
|
|
198
|
+
const result = await this.client.query(sql, queryParams);
|
|
199
|
+
const queryResult = {
|
|
200
|
+
rows: result.rows,
|
|
201
|
+
rowCount: result.rowCount || 0,
|
|
202
|
+
affectedRows: result.rowCount || 0,
|
|
203
|
+
fields: result.fields?.map((field) => ({
|
|
204
|
+
name: field.name,
|
|
205
|
+
type: field.dataTypeID?.toString() || "unknown",
|
|
206
|
+
nullable: true,
|
|
207
|
+
primaryKey: false,
|
|
208
|
+
autoIncrement: false,
|
|
209
|
+
defaultValue: void 0
|
|
210
|
+
})),
|
|
211
|
+
command: result.command || sql.trim().split(" ")[0]?.toUpperCase()
|
|
212
|
+
};
|
|
213
|
+
return queryResult;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
throw new TransactionError(
|
|
216
|
+
`Query failed in transaction: ${error.message}`,
|
|
217
|
+
this.id,
|
|
218
|
+
error
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Alias for query() to provide consistency with adapter's execute method
|
|
224
|
+
*/
|
|
225
|
+
async execute(sql, params, options) {
|
|
226
|
+
return this.query(sql, params, options);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
var PostgreSQLConnectionPool = class {
|
|
230
|
+
constructor(config) {
|
|
231
|
+
this.config = config;
|
|
232
|
+
}
|
|
233
|
+
pool;
|
|
234
|
+
async initialize() {
|
|
235
|
+
try {
|
|
236
|
+
this.pool = new Pool(this.config);
|
|
237
|
+
this.pool.on("error", (err) => {
|
|
238
|
+
console.error("Unexpected error on idle PostgreSQL client", err);
|
|
239
|
+
});
|
|
240
|
+
const client = await this.pool.connect();
|
|
241
|
+
await client.query("SELECT 1");
|
|
242
|
+
client.release();
|
|
243
|
+
} catch (error) {
|
|
244
|
+
throw new ConnectionError("Failed to initialize PostgreSQL connection pool", error);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async getClient() {
|
|
248
|
+
if (!this.pool) {
|
|
249
|
+
throw new ConnectionError("Connection pool not initialized");
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
return await this.pool.connect();
|
|
253
|
+
} catch (error) {
|
|
254
|
+
throw new ConnectionError("Failed to get client from pool", error);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async end() {
|
|
258
|
+
if (this.pool) {
|
|
259
|
+
await this.pool.end();
|
|
260
|
+
this.pool = void 0;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
getStats() {
|
|
264
|
+
if (!this.pool) {
|
|
265
|
+
return {
|
|
266
|
+
total: 0,
|
|
267
|
+
idle: 0,
|
|
268
|
+
active: 0,
|
|
269
|
+
waiting: 0
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
total: this.pool.totalCount,
|
|
274
|
+
idle: this.pool.idleCount,
|
|
275
|
+
active: this.pool.totalCount - this.pool.idleCount,
|
|
276
|
+
waiting: this.pool.waitingCount
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
var PostgreSQLQueryBuilder = class extends BaseQueryBuilder {
|
|
281
|
+
buildSelectSQL() {
|
|
282
|
+
if (!this.fromTable) {
|
|
283
|
+
throw new ValidationError("FROM table is required for SELECT query");
|
|
284
|
+
}
|
|
285
|
+
const parts = [];
|
|
286
|
+
parts.push("SELECT");
|
|
287
|
+
parts.push(this.selectColumns.join(", "));
|
|
288
|
+
parts.push("FROM");
|
|
289
|
+
if (this.fromAlias) {
|
|
290
|
+
parts.push(
|
|
291
|
+
`${this.escapeIdentifierFn(this.fromTable)} AS ${this.escapeIdentifierFn(this.fromAlias)}`
|
|
292
|
+
);
|
|
293
|
+
} else {
|
|
294
|
+
parts.push(this.escapeIdentifierFn(this.fromTable));
|
|
295
|
+
}
|
|
296
|
+
this.joins.forEach((join) => {
|
|
297
|
+
parts.push(`${join.type} JOIN ${join.table} ON ${join.on}`);
|
|
298
|
+
});
|
|
299
|
+
if (this.whereClauses.length > 0) {
|
|
300
|
+
parts.push("WHERE");
|
|
301
|
+
const whereConditions = this.whereClauses.map((clause, index) => {
|
|
302
|
+
const prefix = index === 0 ? "" : clause.type;
|
|
303
|
+
return `${prefix} ${clause.condition}`.trim();
|
|
304
|
+
});
|
|
305
|
+
parts.push(whereConditions.join(" "));
|
|
306
|
+
}
|
|
307
|
+
if (this.groupByColumns.length > 0) {
|
|
308
|
+
parts.push("GROUP BY");
|
|
309
|
+
parts.push(this.groupByColumns.map((col) => this.escapeIdentifierFn(col)).join(", "));
|
|
310
|
+
}
|
|
311
|
+
if (this.havingClause) {
|
|
312
|
+
parts.push("HAVING");
|
|
313
|
+
parts.push(this.havingClause);
|
|
314
|
+
}
|
|
315
|
+
if (this.orderByColumns.length > 0) {
|
|
316
|
+
parts.push("ORDER BY");
|
|
317
|
+
const orderClauses = this.orderByColumns.map(
|
|
318
|
+
({ column, direction }) => `${this.escapeIdentifierFn(column)} ${direction}`
|
|
319
|
+
);
|
|
320
|
+
parts.push(orderClauses.join(", "));
|
|
321
|
+
}
|
|
322
|
+
if (this.limitValue !== void 0) {
|
|
323
|
+
parts.push(`LIMIT ${this.limitValue}`);
|
|
324
|
+
}
|
|
325
|
+
if (this.offsetValue !== void 0) {
|
|
326
|
+
parts.push(`OFFSET ${this.offsetValue}`);
|
|
327
|
+
}
|
|
328
|
+
return { sql: parts.join(" "), bindings: this.bindings };
|
|
329
|
+
}
|
|
330
|
+
buildInsertSQL() {
|
|
331
|
+
if (!this.insertTable || !this.insertData) {
|
|
332
|
+
throw new ValidationError("Table and data are required for INSERT query");
|
|
333
|
+
}
|
|
334
|
+
const dataArray = Array.isArray(this.insertData) ? this.insertData : [this.insertData];
|
|
335
|
+
if (dataArray.length === 0) {
|
|
336
|
+
throw new ValidationError("Cannot insert empty data");
|
|
337
|
+
}
|
|
338
|
+
const firstRow = dataArray[0];
|
|
339
|
+
const columns = Object.keys(firstRow);
|
|
340
|
+
const bindings = [];
|
|
341
|
+
let parameterIndex = 1;
|
|
342
|
+
const valueRows = dataArray.map((row) => {
|
|
343
|
+
const values = columns.map((col) => {
|
|
344
|
+
bindings.push(row[col]);
|
|
345
|
+
return this.parameterPlaceholderFn(parameterIndex++);
|
|
346
|
+
});
|
|
347
|
+
return `(${values.join(", ")})`;
|
|
348
|
+
});
|
|
349
|
+
const sql = `INSERT INTO ${this.escapeIdentifierFn(this.insertTable)} (${columns.map((col) => this.escapeIdentifierFn(col)).join(", ")}) VALUES ${valueRows.join(", ")}`;
|
|
350
|
+
return { sql, bindings };
|
|
351
|
+
}
|
|
352
|
+
buildUpdateSQL() {
|
|
353
|
+
if (!this.updateTable || !this.updateData) {
|
|
354
|
+
throw new ValidationError("Table and data are required for UPDATE query");
|
|
355
|
+
}
|
|
356
|
+
const parts = [];
|
|
357
|
+
const bindings = [];
|
|
358
|
+
let parameterIndex = 1;
|
|
359
|
+
parts.push("UPDATE");
|
|
360
|
+
parts.push(this.escapeIdentifierFn(this.updateTable));
|
|
361
|
+
parts.push("SET");
|
|
362
|
+
const setClauses = Object.entries(this.updateData).map(([key, value]) => {
|
|
363
|
+
bindings.push(value);
|
|
364
|
+
return `${this.escapeIdentifierFn(key)} = ${this.parameterPlaceholderFn(parameterIndex++)}`;
|
|
365
|
+
});
|
|
366
|
+
parts.push(setClauses.join(", "));
|
|
367
|
+
if (this.whereClauses.length > 0) {
|
|
368
|
+
parts.push("WHERE");
|
|
369
|
+
const whereConditions = [];
|
|
370
|
+
this.whereClauses.forEach((clause, index) => {
|
|
371
|
+
let condition = clause.condition;
|
|
372
|
+
for (const binding of clause.bindings) {
|
|
373
|
+
condition = condition.replace("?", this.parameterPlaceholderFn(parameterIndex++));
|
|
374
|
+
bindings.push(binding);
|
|
375
|
+
}
|
|
376
|
+
const prefix = index === 0 ? "" : clause.type;
|
|
377
|
+
whereConditions.push(`${prefix} ${condition}`.trim());
|
|
378
|
+
});
|
|
379
|
+
parts.push(whereConditions.join(" "));
|
|
380
|
+
}
|
|
381
|
+
return { sql: parts.join(" "), bindings };
|
|
382
|
+
}
|
|
383
|
+
buildDeleteSQL() {
|
|
384
|
+
if (!this.deleteTable) {
|
|
385
|
+
throw new ValidationError("Table is required for DELETE query");
|
|
386
|
+
}
|
|
387
|
+
const parts = [];
|
|
388
|
+
const bindings = [];
|
|
389
|
+
let parameterIndex = 1;
|
|
390
|
+
parts.push("DELETE FROM");
|
|
391
|
+
parts.push(this.escapeIdentifierFn(this.deleteTable));
|
|
392
|
+
if (this.whereClauses.length > 0) {
|
|
393
|
+
parts.push("WHERE");
|
|
394
|
+
const whereConditions = [];
|
|
395
|
+
this.whereClauses.forEach((clause, index) => {
|
|
396
|
+
let condition = clause.condition;
|
|
397
|
+
for (const binding of clause.bindings) {
|
|
398
|
+
condition = condition.replace("?", this.parameterPlaceholderFn(parameterIndex++));
|
|
399
|
+
bindings.push(binding);
|
|
400
|
+
}
|
|
401
|
+
const prefix = index === 0 ? "" : clause.type;
|
|
402
|
+
whereConditions.push(`${prefix} ${condition}`.trim());
|
|
403
|
+
});
|
|
404
|
+
parts.push(whereConditions.join(" "));
|
|
405
|
+
}
|
|
406
|
+
return { sql: parts.join(" "), bindings };
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// src/adapter/postgresql-adapter.ts
|
|
411
|
+
var PostgreSQLAdapter = class extends BaseAdapter {
|
|
412
|
+
name = "PostgreSQL";
|
|
413
|
+
version = "2.0.0";
|
|
414
|
+
pool;
|
|
415
|
+
pgOptions;
|
|
416
|
+
parseTypes;
|
|
417
|
+
preparedStatements = /* @__PURE__ */ new Map();
|
|
418
|
+
constructor(options = {}) {
|
|
419
|
+
super(options);
|
|
420
|
+
this.pgOptions = options.pgOptions;
|
|
421
|
+
this.parseTypes = options.parseTypes ?? true;
|
|
422
|
+
if (this.parseTypes) {
|
|
423
|
+
this.configurePgTypes();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
async doConnect(config) {
|
|
427
|
+
const poolConfig = {
|
|
428
|
+
host: config.host,
|
|
429
|
+
port: config.port || 5432,
|
|
430
|
+
user: config.user,
|
|
431
|
+
password: config.password,
|
|
432
|
+
database: config.database,
|
|
433
|
+
max: config.poolSize || 10,
|
|
434
|
+
idleTimeoutMillis: config.idleTimeout || 3e4,
|
|
435
|
+
connectionTimeoutMillis: config.connectionTimeout || 1e4,
|
|
436
|
+
...this.pgOptions
|
|
437
|
+
};
|
|
438
|
+
if (config.ssl) {
|
|
439
|
+
poolConfig.ssl = config.ssl;
|
|
440
|
+
}
|
|
441
|
+
if (config.connectionString) {
|
|
442
|
+
poolConfig.connectionString = config.connectionString;
|
|
443
|
+
}
|
|
444
|
+
this.pool = new PostgreSQLConnectionPool(poolConfig);
|
|
445
|
+
await this.pool.initialize();
|
|
446
|
+
this.logger?.info("Connected to PostgreSQL database", { database: config.database });
|
|
447
|
+
}
|
|
448
|
+
async doDisconnect() {
|
|
449
|
+
if (this.pool) {
|
|
450
|
+
await this.pool.end();
|
|
451
|
+
this.pool = void 0;
|
|
452
|
+
this.preparedStatements.clear();
|
|
453
|
+
this.logger?.info("Disconnected from PostgreSQL database");
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
async doQuery(sql, params, options) {
|
|
457
|
+
if (!this.pool) {
|
|
458
|
+
throw new ConnectionError("Database pool not initialized");
|
|
459
|
+
}
|
|
460
|
+
const client = options?.transaction ? options.transaction.getClient() : await this.pool.getClient();
|
|
461
|
+
try {
|
|
462
|
+
const queryParams = this.normalizeParams(params);
|
|
463
|
+
const result = await client.query(sql, queryParams);
|
|
464
|
+
const queryResult = {
|
|
465
|
+
rows: result.rows,
|
|
466
|
+
rowCount: result.rowCount || 0,
|
|
467
|
+
affectedRows: result.rowCount || 0,
|
|
468
|
+
fields: result.fields?.map((field) => ({
|
|
469
|
+
name: field.name,
|
|
470
|
+
type: this.getFieldType(field.dataTypeID),
|
|
471
|
+
nullable: true,
|
|
472
|
+
primaryKey: false,
|
|
473
|
+
autoIncrement: false,
|
|
474
|
+
defaultValue: void 0
|
|
475
|
+
})),
|
|
476
|
+
command: result.command
|
|
477
|
+
};
|
|
478
|
+
return queryResult;
|
|
479
|
+
} catch (error) {
|
|
480
|
+
throw new QueryError(
|
|
481
|
+
`Query failed: ${error.message}`,
|
|
482
|
+
sql,
|
|
483
|
+
Array.isArray(params) ? params : params ? Object.values(params) : void 0,
|
|
484
|
+
error
|
|
485
|
+
);
|
|
486
|
+
} finally {
|
|
487
|
+
if (!options?.transaction) {
|
|
488
|
+
client.release();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async beginTransaction(options) {
|
|
493
|
+
if (!this.pool) {
|
|
494
|
+
throw new ConnectionError("Database pool not initialized");
|
|
495
|
+
}
|
|
496
|
+
const client = await this.pool.getClient();
|
|
497
|
+
const transaction = new PostgreSQLTransaction(client, options);
|
|
498
|
+
try {
|
|
499
|
+
await transaction.begin();
|
|
500
|
+
this.logger?.debug("Transaction started", { id: transaction.id });
|
|
501
|
+
return transaction;
|
|
502
|
+
} catch (error) {
|
|
503
|
+
client.release();
|
|
504
|
+
throw new TransactionError("Failed to begin transaction", void 0, error);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async prepare(sql, name) {
|
|
508
|
+
if (!this.pool) {
|
|
509
|
+
throw new ConnectionError("Database pool not initialized");
|
|
510
|
+
}
|
|
511
|
+
const client = await this.pool.getClient();
|
|
512
|
+
const stmtName = name || `stmt_${this.preparedStatements.size + 1}`;
|
|
513
|
+
if (!this.preparedStatements.has(stmtName)) {
|
|
514
|
+
this.preparedStatements.set(stmtName, sql);
|
|
515
|
+
}
|
|
516
|
+
return new PostgreSQLPreparedStatement(client, sql, stmtName);
|
|
517
|
+
}
|
|
518
|
+
getPoolStats() {
|
|
519
|
+
if (!this.pool) {
|
|
520
|
+
return {
|
|
521
|
+
total: 0,
|
|
522
|
+
idle: 0,
|
|
523
|
+
active: 0,
|
|
524
|
+
waiting: 0
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
return this.pool.getStats();
|
|
528
|
+
}
|
|
529
|
+
async ping() {
|
|
530
|
+
if (!this.pool) {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
const client = await this.pool.getClient();
|
|
535
|
+
try {
|
|
536
|
+
await client.query("SELECT 1");
|
|
537
|
+
return true;
|
|
538
|
+
} finally {
|
|
539
|
+
client.release();
|
|
540
|
+
}
|
|
541
|
+
} catch {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
escape(value) {
|
|
546
|
+
if (value === null || value === void 0) {
|
|
547
|
+
return "NULL";
|
|
548
|
+
}
|
|
549
|
+
if (typeof value === "string") {
|
|
550
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
551
|
+
}
|
|
552
|
+
if (typeof value === "boolean") {
|
|
553
|
+
return value ? "TRUE" : "FALSE";
|
|
554
|
+
}
|
|
555
|
+
if (value instanceof Date) {
|
|
556
|
+
return `'${value.toISOString()}'`;
|
|
557
|
+
}
|
|
558
|
+
if (typeof value === "object") {
|
|
559
|
+
return `'${JSON.stringify(value).replaceAll("'", "''")}'`;
|
|
560
|
+
}
|
|
561
|
+
return String(value);
|
|
562
|
+
}
|
|
563
|
+
escapeIdentifier(identifier) {
|
|
564
|
+
return `"${identifier.replaceAll('"', '""')}"`;
|
|
565
|
+
}
|
|
566
|
+
createQueryBuilder() {
|
|
567
|
+
return new PostgreSQLQueryBuilder({
|
|
568
|
+
adapter: this,
|
|
569
|
+
escapeIdentifier: (id) => this.escapeIdentifier(id),
|
|
570
|
+
parameterPlaceholder: (index) => `$${index}`,
|
|
571
|
+
crypto: this.crypto
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
normalizeParams(params) {
|
|
575
|
+
if (!params) {
|
|
576
|
+
return [];
|
|
577
|
+
}
|
|
578
|
+
if (Array.isArray(params)) {
|
|
579
|
+
return params;
|
|
580
|
+
}
|
|
581
|
+
return Object.values(params);
|
|
582
|
+
}
|
|
583
|
+
configurePgTypes() {
|
|
584
|
+
types.setTypeParser(types.builtins.INT8, (val) => {
|
|
585
|
+
const num = Number.parseInt(val, 10);
|
|
586
|
+
return Number.isSafeInteger(num) ? num : val;
|
|
587
|
+
});
|
|
588
|
+
types.setTypeParser(types.builtins.FLOAT4, (val) => Number.parseFloat(val));
|
|
589
|
+
types.setTypeParser(types.builtins.FLOAT8, (val) => Number.parseFloat(val));
|
|
590
|
+
types.setTypeParser(types.builtins.NUMERIC, (val) => Number.parseFloat(val));
|
|
591
|
+
types.setTypeParser(types.builtins.DATE, (val) => new Date(val));
|
|
592
|
+
types.setTypeParser(types.builtins.TIMESTAMP, (val) => new Date(val));
|
|
593
|
+
types.setTypeParser(types.builtins.TIMESTAMPTZ, (val) => new Date(val));
|
|
594
|
+
}
|
|
595
|
+
getFieldType(oid) {
|
|
596
|
+
const typeMap = {
|
|
597
|
+
[types.builtins.BOOL]: "boolean",
|
|
598
|
+
[types.builtins.INT2]: "smallint",
|
|
599
|
+
[types.builtins.INT4]: "integer",
|
|
600
|
+
[types.builtins.INT8]: "bigint",
|
|
601
|
+
[types.builtins.FLOAT4]: "real",
|
|
602
|
+
[types.builtins.FLOAT8]: "double",
|
|
603
|
+
[types.builtins.NUMERIC]: "numeric",
|
|
604
|
+
[types.builtins.VARCHAR]: "varchar",
|
|
605
|
+
[types.builtins.TEXT]: "text",
|
|
606
|
+
[types.builtins.DATE]: "date",
|
|
607
|
+
[types.builtins.TIMESTAMP]: "timestamp",
|
|
608
|
+
[types.builtins.TIMESTAMPTZ]: "timestamptz",
|
|
609
|
+
[types.builtins.JSON]: "json",
|
|
610
|
+
[types.builtins.JSONB]: "jsonb",
|
|
611
|
+
[types.builtins.UUID]: "uuid"
|
|
612
|
+
};
|
|
613
|
+
return typeMap[oid] || "unknown";
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
export { PostgreSQLAdapter, PostgreSQLConnectionPool, PostgreSQLPreparedStatement, PostgreSQLQueryBuilder, PostgreSQLTransaction };
|
|
618
|
+
//# sourceMappingURL=index.js.map
|
|
619
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapter/postgresql-prepared-statement.ts","../src/adapter/postgresql-transaction.ts","../src/pool/connection-pool.ts","../src/query-builder/postgresql-query-builder.ts","../src/adapter/postgresql-adapter.ts"],"names":["ConnectionError","QueryError","TransactionError"],"mappings":"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","file":"index.js","sourcesContent":["import { QueryError } from '@db-bridge/core';\n\nimport type { PreparedStatement, QueryResult } from '@db-bridge/core';\nimport type { PoolClient, QueryResult as PgQueryResult } from 'pg';\n\nexport class PostgreSQLPreparedStatement<T = unknown> implements PreparedStatement<T> {\n private released = false;\n\n constructor(\n private client: PoolClient,\n private sql: string,\n private name: string,\n ) {}\n\n async execute(params?: unknown[]): Promise<QueryResult<T>> {\n if (this.released) {\n throw new QueryError('Prepared statement has been released');\n }\n\n try {\n // PostgreSQL doesn't require separate prepare step when using named queries\n\n const result: PgQueryResult = await this.client.query({\n text: this.sql,\n name: this.name,\n values: params || [],\n });\n\n return {\n rows: result.rows as T[],\n rowCount: result.rowCount || 0,\n affectedRows: result.rowCount || 0,\n fields: result.fields?.map((field) => ({\n name: field.name,\n type: field.dataTypeID.toString(),\n nullable: true,\n primaryKey: false,\n autoIncrement: false,\n defaultValue: undefined,\n })),\n command: result.command,\n };\n } catch (error) {\n throw new QueryError(\n `Prepared statement execution failed: ${(error as Error).message}`,\n this.sql,\n params,\n error as Error,\n );\n }\n }\n\n async release(): Promise<void> {\n if (this.released) {\n return;\n }\n\n try {\n // PostgreSQL automatically deallocates named queries when connection is released\n this.client.release();\n this.released = true;\n } catch (error) {\n throw new QueryError(\n `Failed to release prepared statement: ${(error as Error).message}`,\n this.sql,\n undefined,\n error as Error,\n );\n }\n }\n\n /**\n * Alias for release() - industry standard naming\n */\n async close(): Promise<void> {\n return this.release();\n }\n}\n","import { TransactionError, generateUUID } from '@db-bridge/core';\n\nimport type {\n Transaction,\n TransactionOptions,\n QueryParams,\n QueryOptions,\n QueryResult,\n} from '@db-bridge/core';\nimport type { PoolClient } from 'pg';\n\nexport class PostgreSQLTransaction implements Transaction {\n readonly id: string;\n private _isActive = false;\n private savepoints: Set<string> = new Set();\n\n constructor(\n private client: PoolClient,\n private options?: TransactionOptions,\n ) {\n this.id = generateUUID();\n }\n\n get isActive(): boolean {\n return this._isActive;\n }\n\n async begin(): Promise<void> {\n if (this._isActive) {\n throw new TransactionError('Transaction already active', this.id);\n }\n\n try {\n const queries: string[] = [];\n\n if (this.options?.isolationLevel) {\n queries.push(`SET TRANSACTION ISOLATION LEVEL ${this.options.isolationLevel}`);\n }\n\n if (this.options?.readOnly) {\n queries.push('SET TRANSACTION READ ONLY');\n } else {\n queries.push('SET TRANSACTION READ WRITE');\n }\n\n if (this.options?.deferrable) {\n queries.push('SET TRANSACTION DEFERRABLE');\n }\n\n await this.client.query('BEGIN');\n\n for (const query of queries) {\n await this.client.query(query);\n }\n\n this._isActive = true;\n } catch (error) {\n throw new TransactionError('Failed to begin transaction', this.id, error as Error);\n }\n }\n\n async commit(): Promise<void> {\n if (!this._isActive) {\n throw new TransactionError('Transaction not active', this.id);\n }\n\n try {\n await this.client.query('COMMIT');\n this._isActive = false;\n this.savepoints.clear();\n } catch (error) {\n throw new TransactionError('Failed to commit transaction', this.id, error as Error);\n } finally {\n this.client.release();\n }\n }\n\n async rollback(): Promise<void> {\n if (!this._isActive) {\n throw new TransactionError('Transaction not active', this.id);\n }\n\n try {\n await this.client.query('ROLLBACK');\n this._isActive = false;\n this.savepoints.clear();\n } catch (error) {\n throw new TransactionError('Failed to rollback transaction', this.id, error as Error);\n } finally {\n this.client.release();\n }\n }\n\n async savepoint(name: string): Promise<void> {\n if (!this._isActive) {\n throw new TransactionError('Transaction not active', this.id);\n }\n\n if (this.savepoints.has(name)) {\n throw new TransactionError(`Savepoint \"${name}\" already exists`, this.id);\n }\n\n try {\n const safeName = name.replaceAll(/\\W/g, '_');\n await this.client.query(`SAVEPOINT ${safeName}`);\n this.savepoints.add(name);\n } catch (error) {\n throw new TransactionError(`Failed to create savepoint \"${name}\"`, this.id, error as Error);\n }\n }\n\n async releaseSavepoint(name: string): Promise<void> {\n if (!this._isActive) {\n throw new TransactionError('Transaction not active', this.id);\n }\n\n if (!this.savepoints.has(name)) {\n throw new TransactionError(`Savepoint \"${name}\" does not exist`, this.id);\n }\n\n try {\n const safeName = name.replaceAll(/\\W/g, '_');\n await this.client.query(`RELEASE SAVEPOINT ${safeName}`);\n this.savepoints.delete(name);\n } catch (error) {\n throw new TransactionError(`Failed to release savepoint \"${name}\"`, this.id, error as Error);\n }\n }\n\n async rollbackToSavepoint(name: string): Promise<void> {\n if (!this._isActive) {\n throw new TransactionError('Transaction not active', this.id);\n }\n\n if (!this.savepoints.has(name)) {\n throw new TransactionError(`Savepoint \"${name}\" does not exist`, this.id);\n }\n\n try {\n const safeName = name.replaceAll(/\\W/g, '_');\n await this.client.query(`ROLLBACK TO SAVEPOINT ${safeName}`);\n\n const savepointsArray = Array.from(this.savepoints);\n const index = savepointsArray.indexOf(name);\n if (index !== -1) {\n for (let i = index + 1; i < savepointsArray.length; i++) {\n this.savepoints.delete(savepointsArray[i]!);\n }\n }\n } catch (error) {\n throw new TransactionError(\n `Failed to rollback to savepoint \"${name}\"`,\n this.id,\n error as Error,\n );\n }\n }\n\n getClient(): PoolClient {\n return this.client;\n }\n\n async query<T = unknown>(\n sql: string,\n params?: QueryParams,\n _options?: QueryOptions,\n ): Promise<QueryResult<T>> {\n if (!this._isActive) {\n throw new TransactionError('Transaction not active', this.id);\n }\n\n try {\n const queryParams = Array.isArray(params) ? params : params ? Object.values(params) : [];\n const result = await this.client.query(sql, queryParams);\n\n const queryResult: QueryResult<T> = {\n rows: result.rows as T[],\n rowCount: result.rowCount || 0,\n affectedRows: result.rowCount || 0,\n fields: result.fields?.map((field) => ({\n name: field.name,\n type: field.dataTypeID?.toString() || 'unknown',\n nullable: true,\n primaryKey: false,\n autoIncrement: false,\n defaultValue: undefined,\n })),\n command: result.command || sql.trim().split(' ')[0]?.toUpperCase(),\n };\n\n return queryResult;\n } catch (error) {\n throw new TransactionError(\n `Query failed in transaction: ${(error as Error).message}`,\n this.id,\n error as Error,\n );\n }\n }\n\n /**\n * Alias for query() to provide consistency with adapter's execute method\n */\n async execute<T = unknown>(\n sql: string,\n params?: QueryParams,\n options?: QueryOptions,\n ): Promise<QueryResult<T>> {\n return this.query<T>(sql, params, options);\n }\n}\n","import { ConnectionError } from '@db-bridge/core';\nimport { Pool } from 'pg';\n\nimport type { PoolStats } from '@db-bridge/core';\nimport type { PoolClient, PoolConfig } from 'pg';\n\nexport class PostgreSQLConnectionPool {\n private pool?: Pool;\n\n constructor(private config: PoolConfig) {}\n\n async initialize(): Promise<void> {\n try {\n this.pool = new Pool(this.config);\n\n this.pool.on('error', (err) => {\n console.error('Unexpected error on idle PostgreSQL client', err);\n });\n\n const client = await this.pool.connect();\n await client.query('SELECT 1');\n client.release();\n } catch (error) {\n throw new ConnectionError('Failed to initialize PostgreSQL connection pool', error as Error);\n }\n }\n\n async getClient(): Promise<PoolClient> {\n if (!this.pool) {\n throw new ConnectionError('Connection pool not initialized');\n }\n\n try {\n return await this.pool.connect();\n } catch (error) {\n throw new ConnectionError('Failed to get client from pool', error as Error);\n }\n }\n\n async end(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n this.pool = undefined;\n }\n }\n\n getStats(): PoolStats {\n if (!this.pool) {\n return {\n total: 0,\n idle: 0,\n active: 0,\n waiting: 0,\n };\n }\n\n return {\n total: this.pool.totalCount,\n idle: this.pool.idleCount,\n active: this.pool.totalCount - this.pool.idleCount,\n waiting: this.pool.waitingCount,\n };\n }\n}\n","import { BaseQueryBuilder, ValidationError } from '@db-bridge/core';\n\nexport class PostgreSQLQueryBuilder<T = unknown> extends BaseQueryBuilder<T> {\n protected buildSelectSQL(): { sql: string; bindings: unknown[] } {\n if (!this.fromTable) {\n throw new ValidationError('FROM table is required for SELECT query');\n }\n\n const parts: string[] = [];\n\n parts.push('SELECT');\n parts.push(this.selectColumns.join(', '));\n\n parts.push('FROM');\n if (this.fromAlias) {\n parts.push(\n `${this.escapeIdentifierFn(this.fromTable)} AS ${this.escapeIdentifierFn(this.fromAlias)}`,\n );\n } else {\n parts.push(this.escapeIdentifierFn(this.fromTable));\n }\n\n this.joins.forEach((join) => {\n parts.push(`${join.type} JOIN ${join.table} ON ${join.on}`);\n });\n\n if (this.whereClauses.length > 0) {\n parts.push('WHERE');\n const whereConditions = this.whereClauses.map((clause, index) => {\n const prefix = index === 0 ? '' : clause.type;\n return `${prefix} ${clause.condition}`.trim();\n });\n parts.push(whereConditions.join(' '));\n }\n\n if (this.groupByColumns.length > 0) {\n parts.push('GROUP BY');\n parts.push(this.groupByColumns.map((col) => this.escapeIdentifierFn(col)).join(', '));\n }\n\n if (this.havingClause) {\n parts.push('HAVING');\n parts.push(this.havingClause);\n }\n\n if (this.orderByColumns.length > 0) {\n parts.push('ORDER BY');\n const orderClauses = this.orderByColumns.map(\n ({ column, direction }) => `${this.escapeIdentifierFn(column)} ${direction}`,\n );\n parts.push(orderClauses.join(', '));\n }\n\n if (this.limitValue !== undefined) {\n parts.push(`LIMIT ${this.limitValue}`);\n }\n\n if (this.offsetValue !== undefined) {\n parts.push(`OFFSET ${this.offsetValue}`);\n }\n\n return { sql: parts.join(' '), bindings: this.bindings };\n }\n\n protected buildInsertSQL(): { sql: string; bindings: unknown[] } {\n if (!this.insertTable || !this.insertData) {\n throw new ValidationError('Table and data are required for INSERT query');\n }\n\n const dataArray = Array.isArray(this.insertData) ? this.insertData : [this.insertData];\n if (dataArray.length === 0) {\n throw new ValidationError('Cannot insert empty data');\n }\n\n const firstRow = dataArray[0]!;\n const columns = Object.keys(firstRow);\n const bindings: unknown[] = [];\n let parameterIndex = 1;\n\n const valueRows = dataArray.map((row) => {\n const values = columns.map((col) => {\n bindings.push(row[col]);\n return this.parameterPlaceholderFn(parameterIndex++);\n });\n return `(${values.join(', ')})`;\n });\n\n const sql = `INSERT INTO ${this.escapeIdentifierFn(this.insertTable)} (${columns\n .map((col) => this.escapeIdentifierFn(col))\n .join(', ')}) VALUES ${valueRows.join(', ')}`;\n\n return { sql, bindings };\n }\n\n protected buildUpdateSQL(): { sql: string; bindings: unknown[] } {\n if (!this.updateTable || !this.updateData) {\n throw new ValidationError('Table and data are required for UPDATE query');\n }\n\n const parts: string[] = [];\n const bindings: unknown[] = [];\n let parameterIndex = 1;\n\n parts.push('UPDATE');\n parts.push(this.escapeIdentifierFn(this.updateTable));\n parts.push('SET');\n\n const setClauses = Object.entries(this.updateData).map(([key, value]) => {\n bindings.push(value);\n return `${this.escapeIdentifierFn(key)} = ${this.parameterPlaceholderFn(parameterIndex++)}`;\n });\n parts.push(setClauses.join(', '));\n\n if (this.whereClauses.length > 0) {\n parts.push('WHERE');\n\n const whereConditions: string[] = [];\n this.whereClauses.forEach((clause, index) => {\n let condition = clause.condition;\n\n for (const binding of clause.bindings) {\n condition = condition.replace('?', this.parameterPlaceholderFn(parameterIndex++));\n bindings.push(binding);\n }\n\n const prefix = index === 0 ? '' : clause.type;\n whereConditions.push(`${prefix} ${condition}`.trim());\n });\n\n parts.push(whereConditions.join(' '));\n }\n\n return { sql: parts.join(' '), bindings };\n }\n\n protected buildDeleteSQL(): { sql: string; bindings: unknown[] } {\n if (!this.deleteTable) {\n throw new ValidationError('Table is required for DELETE query');\n }\n\n const parts: string[] = [];\n const bindings: unknown[] = [];\n let parameterIndex = 1;\n\n parts.push('DELETE FROM');\n parts.push(this.escapeIdentifierFn(this.deleteTable));\n\n if (this.whereClauses.length > 0) {\n parts.push('WHERE');\n\n const whereConditions: string[] = [];\n this.whereClauses.forEach((clause, index) => {\n let condition = clause.condition;\n\n for (const binding of clause.bindings) {\n condition = condition.replace('?', this.parameterPlaceholderFn(parameterIndex++));\n bindings.push(binding);\n }\n\n const prefix = index === 0 ? '' : clause.type;\n whereConditions.push(`${prefix} ${condition}`.trim());\n });\n\n parts.push(whereConditions.join(' '));\n }\n\n return { sql: parts.join(' '), bindings };\n }\n}\n","import { BaseAdapter, ConnectionError, QueryError, TransactionError } from '@db-bridge/core';\nimport { types } from 'pg';\n\nimport { PostgreSQLPreparedStatement } from './postgresql-prepared-statement';\nimport { PostgreSQLTransaction } from './postgresql-transaction';\nimport { PostgreSQLConnectionPool } from '../pool/connection-pool';\nimport { PostgreSQLQueryBuilder } from '../query-builder/postgresql-query-builder';\n\nimport type {\n BaseAdapterOptions,\n ConnectionConfig,\n QueryResult,\n QueryOptions,\n QueryParams,\n Transaction,\n TransactionOptions,\n PreparedStatement,\n PoolStats,\n} from '@db-bridge/core';\nimport type { PoolConfig } from 'pg';\n\nexport interface PostgreSQLAdapterOptions extends BaseAdapterOptions {\n pgOptions?: PoolConfig;\n parseTypes?: boolean;\n}\n\nexport class PostgreSQLAdapter extends BaseAdapter {\n readonly name = 'PostgreSQL';\n readonly version = '2.0.0';\n\n private pool?: PostgreSQLConnectionPool;\n private readonly pgOptions?: PoolConfig;\n private readonly parseTypes: boolean;\n private preparedStatements: Map<string, string> = new Map();\n\n constructor(options: PostgreSQLAdapterOptions = {}) {\n super(options);\n this.pgOptions = options.pgOptions;\n this.parseTypes = options.parseTypes ?? true;\n\n if (this.parseTypes) {\n this.configurePgTypes();\n }\n }\n\n protected async doConnect(config: ConnectionConfig): Promise<void> {\n const poolConfig: PoolConfig = {\n host: config.host,\n port: config.port || 5432,\n user: config.user,\n password: config.password,\n database: config.database,\n max: config.poolSize || 10,\n idleTimeoutMillis: config.idleTimeout || 30000,\n connectionTimeoutMillis: config.connectionTimeout || 10000,\n ...this.pgOptions,\n };\n\n if (config.ssl) {\n poolConfig.ssl = config.ssl;\n }\n\n if (config.connectionString) {\n poolConfig.connectionString = config.connectionString;\n }\n\n this.pool = new PostgreSQLConnectionPool(poolConfig);\n await this.pool.initialize();\n\n this.logger?.info('Connected to PostgreSQL database', { database: config.database });\n }\n\n protected async doDisconnect(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n this.pool = undefined;\n this.preparedStatements.clear();\n this.logger?.info('Disconnected from PostgreSQL database');\n }\n }\n\n protected async doQuery<T = unknown>(\n sql: string,\n params?: QueryParams,\n options?: QueryOptions,\n ): Promise<QueryResult<T>> {\n if (!this.pool) {\n throw new ConnectionError('Database pool not initialized');\n }\n\n const client = options?.transaction\n ? (options.transaction as PostgreSQLTransaction).getClient()\n : await this.pool.getClient();\n\n try {\n const queryParams = this.normalizeParams(params);\n const result = await client.query(sql, queryParams);\n\n const queryResult: QueryResult<T> = {\n rows: result.rows as T[],\n rowCount: result.rowCount || 0,\n affectedRows: result.rowCount || 0,\n fields: result.fields?.map((field) => ({\n name: field.name,\n type: this.getFieldType(field.dataTypeID),\n nullable: true,\n primaryKey: false,\n autoIncrement: false,\n defaultValue: undefined,\n })),\n command: result.command,\n };\n\n return queryResult;\n } catch (error) {\n throw new QueryError(\n `Query failed: ${(error as Error).message}`,\n sql,\n Array.isArray(params) ? params : params ? Object.values(params) : undefined,\n error as Error,\n );\n } finally {\n if (!options?.transaction) {\n client.release();\n }\n }\n }\n\n override async beginTransaction(options?: TransactionOptions): Promise<Transaction> {\n if (!this.pool) {\n throw new ConnectionError('Database pool not initialized');\n }\n\n const client = await this.pool.getClient();\n const transaction = new PostgreSQLTransaction(client, options);\n\n try {\n await transaction.begin();\n this.logger?.debug('Transaction started', { id: transaction.id });\n return transaction;\n } catch (error) {\n client.release();\n throw new TransactionError('Failed to begin transaction', undefined, error as Error);\n }\n }\n\n override async prepare<T = unknown>(sql: string, name?: string): Promise<PreparedStatement<T>> {\n if (!this.pool) {\n throw new ConnectionError('Database pool not initialized');\n }\n\n const client = await this.pool.getClient();\n const stmtName = name || `stmt_${this.preparedStatements.size + 1}`;\n\n if (!this.preparedStatements.has(stmtName)) {\n this.preparedStatements.set(stmtName, sql);\n }\n\n return new PostgreSQLPreparedStatement<T>(client, sql, stmtName);\n }\n\n override getPoolStats(): PoolStats {\n if (!this.pool) {\n return {\n total: 0,\n idle: 0,\n active: 0,\n waiting: 0,\n };\n }\n\n return this.pool.getStats();\n }\n\n override async ping(): Promise<boolean> {\n if (!this.pool) {\n return false;\n }\n\n try {\n const client = await this.pool.getClient();\n try {\n await client.query('SELECT 1');\n return true;\n } finally {\n client.release();\n }\n } catch {\n return false;\n }\n }\n\n override escape(value: unknown): string {\n if (value === null || value === undefined) {\n return 'NULL';\n }\n\n if (typeof value === 'string') {\n return `'${value.replaceAll(\"'\", \"''\")}'`;\n }\n\n if (typeof value === 'boolean') {\n return value ? 'TRUE' : 'FALSE';\n }\n\n if (value instanceof Date) {\n return `'${value.toISOString()}'`;\n }\n\n if (typeof value === 'object') {\n return `'${JSON.stringify(value).replaceAll(\"'\", \"''\")}'`;\n }\n\n return String(value);\n }\n\n override escapeIdentifier(identifier: string): string {\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\n }\n\n createQueryBuilder<T = unknown>(): PostgreSQLQueryBuilder<T> {\n return new PostgreSQLQueryBuilder<T>({\n adapter: this,\n escapeIdentifier: (id) => this.escapeIdentifier(id),\n parameterPlaceholder: (index) => `$${index}`,\n crypto: this.crypto,\n });\n }\n\n private normalizeParams(params?: QueryParams): unknown[] {\n if (!params) {\n return [];\n }\n\n if (Array.isArray(params)) {\n return params;\n }\n\n return Object.values(params);\n }\n\n private configurePgTypes(): void {\n types.setTypeParser(types.builtins.INT8, (val: string) => {\n const num = Number.parseInt(val, 10);\n return Number.isSafeInteger(num) ? num : val;\n });\n\n types.setTypeParser(types.builtins.FLOAT4, (val: string) => Number.parseFloat(val));\n types.setTypeParser(types.builtins.FLOAT8, (val: string) => Number.parseFloat(val));\n types.setTypeParser(types.builtins.NUMERIC, (val: string) => Number.parseFloat(val));\n\n types.setTypeParser(types.builtins.DATE, (val: string) => new Date(val));\n types.setTypeParser(types.builtins.TIMESTAMP, (val: string) => new Date(val));\n types.setTypeParser(types.builtins.TIMESTAMPTZ, (val: string) => new Date(val));\n }\n\n private getFieldType(oid: number): string {\n const typeMap: Record<number, string> = {\n [types.builtins.BOOL]: 'boolean',\n [types.builtins.INT2]: 'smallint',\n [types.builtins.INT4]: 'integer',\n [types.builtins.INT8]: 'bigint',\n [types.builtins.FLOAT4]: 'real',\n [types.builtins.FLOAT8]: 'double',\n [types.builtins.NUMERIC]: 'numeric',\n [types.builtins.VARCHAR]: 'varchar',\n [types.builtins.TEXT]: 'text',\n [types.builtins.DATE]: 'date',\n [types.builtins.TIMESTAMP]: 'timestamp',\n [types.builtins.TIMESTAMPTZ]: 'timestamptz',\n [types.builtins.JSON]: 'json',\n [types.builtins.JSONB]: 'jsonb',\n [types.builtins.UUID]: 'uuid',\n };\n\n return typeMap[oid] || 'unknown';\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "@db-bridge/postgresql",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "PostgreSQL adapter for db-bridge",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"test": "vitest",
|
|
22
|
+
"test:coverage": "vitest --coverage",
|
|
23
|
+
"lint": "eslint src --ext .ts",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"clean": "rimraf dist"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@db-bridge/core": "^1.0.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"pg": "^8.11.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"pg": {
|
|
35
|
+
"optional": false
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.0.0",
|
|
40
|
+
"@types/pg": "^8.11.0",
|
|
41
|
+
"@vitest/coverage-v8": "^3.0.0",
|
|
42
|
+
"pg": "^8.17.0",
|
|
43
|
+
"tsup": "^8.0.1",
|
|
44
|
+
"typescript": "^5.3.0",
|
|
45
|
+
"vitest": "^3.0.0"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=16.0.0"
|
|
49
|
+
},
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"gitHead": "7aaca822d0d43120f453562de592e48e7d3e8d32"
|
|
55
|
+
}
|