@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 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
@@ -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
+ }