@db-bridge/mysql 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 +356 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +623 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -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,356 @@
|
|
|
1
|
+
# @db-bridge/mysql
|
|
2
|
+
|
|
3
|
+
MySQL adapter for DB Bridge - A comprehensive database management library.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @db-bridge/mysql @db-bridge/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Full MySQL and MariaDB support
|
|
14
|
+
- Connection pooling with mysql2
|
|
15
|
+
- Prepared statements
|
|
16
|
+
- Transaction management with savepoints
|
|
17
|
+
- Bulk operations
|
|
18
|
+
- TypeScript support
|
|
19
|
+
- Query builder integration
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { MySQLAdapter } from '@db-bridge/mysql';
|
|
25
|
+
|
|
26
|
+
const adapter = new MySQLAdapter();
|
|
27
|
+
|
|
28
|
+
// Connect
|
|
29
|
+
await adapter.connect({
|
|
30
|
+
host: 'localhost',
|
|
31
|
+
port: 3306,
|
|
32
|
+
user: 'root',
|
|
33
|
+
password: 'password',
|
|
34
|
+
database: 'myapp',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Simple query
|
|
38
|
+
const users = await adapter.query('SELECT * FROM users WHERE active = ?', [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 MySQLConnectionConfig {
|
|
60
|
+
host: string;
|
|
61
|
+
port?: number;
|
|
62
|
+
user: string;
|
|
63
|
+
password: string;
|
|
64
|
+
database: string;
|
|
65
|
+
ssl?: SslOptions;
|
|
66
|
+
timezone?: string;
|
|
67
|
+
charset?: string;
|
|
68
|
+
connectionLimit?: number;
|
|
69
|
+
queueLimit?: number;
|
|
70
|
+
waitForConnections?: boolean;
|
|
71
|
+
connectTimeout?: number;
|
|
72
|
+
timeout?: number;
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Adapter Options
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const adapter = new MySQLAdapter({
|
|
80
|
+
// Logger instance (optional)
|
|
81
|
+
logger: console,
|
|
82
|
+
|
|
83
|
+
// Retry options
|
|
84
|
+
retryOptions: {
|
|
85
|
+
maxRetries: 3,
|
|
86
|
+
retryDelay: 1000,
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// MySQL2 specific options
|
|
90
|
+
mysql2Options: {
|
|
91
|
+
ssl: {
|
|
92
|
+
ca: fs.readFileSync('ca-cert.pem'),
|
|
93
|
+
rejectUnauthorized: true,
|
|
94
|
+
},
|
|
95
|
+
timezone: '+00:00',
|
|
96
|
+
connectionLimit: 10,
|
|
97
|
+
enableKeepAlive: true,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Usage
|
|
103
|
+
|
|
104
|
+
### Basic Queries
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// Simple query
|
|
108
|
+
const result = await adapter.query('SELECT * FROM products WHERE price > ?', [100]);
|
|
109
|
+
|
|
110
|
+
// Named placeholders (converted to ?)
|
|
111
|
+
const users = await adapter.query('SELECT * FROM users WHERE role = :role AND status = :status', {
|
|
112
|
+
role: 'admin',
|
|
113
|
+
status: 'active',
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Insert with auto-generated ID
|
|
117
|
+
const insertResult = await adapter.execute('INSERT INTO users (name, email) VALUES (?, ?)', [
|
|
118
|
+
'John Doe',
|
|
119
|
+
'john@example.com',
|
|
120
|
+
]);
|
|
121
|
+
console.log('Inserted ID:', insertResult.insertId);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Query Builder
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const qb = adapter.createQueryBuilder();
|
|
128
|
+
|
|
129
|
+
// SELECT with joins
|
|
130
|
+
const orders = await qb
|
|
131
|
+
.select('o.*', 'u.name as user_name', 'u.email')
|
|
132
|
+
.from('orders', 'o')
|
|
133
|
+
.join('users u', 'u.id = o.user_id')
|
|
134
|
+
.where('o.status', '=', 'pending')
|
|
135
|
+
.where('o.created_at', '>', '2024-01-01')
|
|
136
|
+
.orderBy('o.created_at', 'DESC')
|
|
137
|
+
.limit(20)
|
|
138
|
+
.execute();
|
|
139
|
+
|
|
140
|
+
// INSERT
|
|
141
|
+
await qb
|
|
142
|
+
.insert('products', {
|
|
143
|
+
name: 'New Product',
|
|
144
|
+
price: 99.99,
|
|
145
|
+
stock: 100,
|
|
146
|
+
})
|
|
147
|
+
.execute();
|
|
148
|
+
|
|
149
|
+
// UPDATE
|
|
150
|
+
await qb.update('products', { price: 89.99 }).where('id', '=', 123).execute();
|
|
151
|
+
|
|
152
|
+
// DELETE
|
|
153
|
+
await qb.delete('products').where('discontinued', '=', true).execute();
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Transactions
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// Basic transaction
|
|
160
|
+
const transaction = await adapter.beginTransaction();
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
await adapter.execute('INSERT INTO accounts (name, balance) VALUES (?, ?)', ['Checking', 1000], {
|
|
164
|
+
transaction,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await adapter.execute('INSERT INTO transactions (account_id, amount) VALUES (?, ?)', [1, 1000], {
|
|
168
|
+
transaction,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
await transaction.commit();
|
|
172
|
+
} catch (error) {
|
|
173
|
+
await transaction.rollback();
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Transaction with savepoints
|
|
178
|
+
const tx = await adapter.beginTransaction();
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
await adapter.execute('INSERT INTO logs ...', [], { transaction: tx });
|
|
182
|
+
|
|
183
|
+
await tx.savepoint('sp1');
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
await riskyOperation(tx);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
await tx.rollbackToSavepoint('sp1');
|
|
189
|
+
// Continue with transaction
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
await tx.commit();
|
|
193
|
+
} catch (error) {
|
|
194
|
+
await tx.rollback();
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Prepared Statements
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// Prepare statement
|
|
202
|
+
const stmt = await adapter.prepare('SELECT * FROM users WHERE department = ? AND role = ?');
|
|
203
|
+
|
|
204
|
+
// Execute multiple times
|
|
205
|
+
const sales = await stmt.execute(['sales', 'manager']);
|
|
206
|
+
const engineering = await stmt.execute(['engineering', 'developer']);
|
|
207
|
+
const hr = await stmt.execute(['hr', 'recruiter']);
|
|
208
|
+
|
|
209
|
+
// Release when done
|
|
210
|
+
await stmt.release();
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Bulk Operations
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
// Bulk insert
|
|
217
|
+
const users = [
|
|
218
|
+
{ name: 'User 1', email: 'user1@example.com' },
|
|
219
|
+
{ name: 'User 2', email: 'user2@example.com' },
|
|
220
|
+
{ name: 'User 3', email: 'user3@example.com' },
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
await adapter.createQueryBuilder().insert('users', users).execute();
|
|
224
|
+
|
|
225
|
+
// Bulk update
|
|
226
|
+
await adapter.execute(`
|
|
227
|
+
INSERT INTO products (id, price) VALUES
|
|
228
|
+
(1, 10.99), (2, 20.99), (3, 30.99)
|
|
229
|
+
ON DUPLICATE KEY UPDATE price = VALUES(price)
|
|
230
|
+
`);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Connection Pool Management
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
// Monitor pool stats
|
|
237
|
+
const stats = adapter.getPoolStats();
|
|
238
|
+
console.log('Active connections:', stats.active);
|
|
239
|
+
console.log('Idle connections:', stats.idle);
|
|
240
|
+
console.log('Queue length:', stats.waiting);
|
|
241
|
+
|
|
242
|
+
// Check connection
|
|
243
|
+
const isAlive = await adapter.ping();
|
|
244
|
+
console.log('Connection alive:', isAlive);
|
|
245
|
+
|
|
246
|
+
// Force close all connections
|
|
247
|
+
await adapter.disconnect();
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Error Handling
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { ConnectionError, QueryError } from '@db-bridge/core';
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
await adapter.connect(config);
|
|
257
|
+
} catch (error) {
|
|
258
|
+
if (error instanceof ConnectionError) {
|
|
259
|
+
console.error('Failed to connect:', error.message);
|
|
260
|
+
// Handle connection error
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
await adapter.query('SELECT * FROM non_existent_table');
|
|
266
|
+
} catch (error) {
|
|
267
|
+
if (error instanceof QueryError) {
|
|
268
|
+
console.error('Query error:', {
|
|
269
|
+
code: error.code,
|
|
270
|
+
message: error.message,
|
|
271
|
+
sql: error.sql,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## MySQL-Specific Features
|
|
278
|
+
|
|
279
|
+
### JSON Support
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
// Insert JSON data
|
|
283
|
+
await adapter.execute('INSERT INTO logs (data) VALUES (?)', [
|
|
284
|
+
JSON.stringify({ action: 'login', user: 123 }),
|
|
285
|
+
]);
|
|
286
|
+
|
|
287
|
+
// Query JSON fields
|
|
288
|
+
const logs = await adapter.query("SELECT * FROM logs WHERE JSON_EXTRACT(data, '$.action') = ?", [
|
|
289
|
+
'login',
|
|
290
|
+
]);
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Full-Text Search
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
const results = await adapter.query(
|
|
297
|
+
'SELECT * FROM articles WHERE MATCH(title, content) AGAINST(? IN NATURAL LANGUAGE MODE)',
|
|
298
|
+
['database management'],
|
|
299
|
+
);
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Spatial Data
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// Insert point
|
|
306
|
+
await adapter.execute('INSERT INTO locations (name, coords) VALUES (?, ST_GeomFromText(?))', [
|
|
307
|
+
'Office',
|
|
308
|
+
'POINT(40.7128 -74.0060)',
|
|
309
|
+
]);
|
|
310
|
+
|
|
311
|
+
// Find nearby
|
|
312
|
+
const nearby = await adapter.query(
|
|
313
|
+
`
|
|
314
|
+
SELECT name, ST_Distance_Sphere(coords, ST_GeomFromText(?)) as distance
|
|
315
|
+
FROM locations
|
|
316
|
+
HAVING distance < 1000
|
|
317
|
+
ORDER BY distance
|
|
318
|
+
`,
|
|
319
|
+
['POINT(40.7589 -73.9851)'],
|
|
320
|
+
);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## Best Practices
|
|
324
|
+
|
|
325
|
+
1. **Use Connection Pooling**: Always use connection pooling in production
|
|
326
|
+
2. **Handle Errors**: Implement proper error handling and retry logic
|
|
327
|
+
3. **Use Prepared Statements**: For repeated queries with different parameters
|
|
328
|
+
4. **Set Timeouts**: Configure appropriate connection and query timeouts
|
|
329
|
+
5. **Monitor Pool Stats**: Keep track of connection pool health
|
|
330
|
+
6. **Use Transactions**: For data consistency across multiple operations
|
|
331
|
+
|
|
332
|
+
## TypeScript Support
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
interface User {
|
|
336
|
+
id: number;
|
|
337
|
+
name: string;
|
|
338
|
+
email: string;
|
|
339
|
+
created_at: Date;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Type-safe queries
|
|
343
|
+
const users = await adapter.query<User>('SELECT * FROM users WHERE id = ?', [123]);
|
|
344
|
+
|
|
345
|
+
// Type-safe query builder
|
|
346
|
+
const user = await adapter
|
|
347
|
+
.createQueryBuilder<User>()
|
|
348
|
+
.select('*')
|
|
349
|
+
.from('users')
|
|
350
|
+
.where('id', '=', 123)
|
|
351
|
+
.first();
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
MIT © Berke Erdoğan
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
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 * as mysql from 'mysql2/promise';
|
|
4
|
+
|
|
5
|
+
declare class MySQLQueryBuilder<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 MySQLAdapterOptions extends BaseAdapterOptions {
|
|
25
|
+
mysql2Options?: mysql.ConnectionOptions;
|
|
26
|
+
}
|
|
27
|
+
declare class MySQLAdapter extends BaseAdapter {
|
|
28
|
+
readonly name = "MySQL";
|
|
29
|
+
readonly version = "2.0.0";
|
|
30
|
+
private pool?;
|
|
31
|
+
private readonly mysql2Options?;
|
|
32
|
+
private poolConfig;
|
|
33
|
+
constructor(options?: MySQLAdapterOptions);
|
|
34
|
+
protected doConnect(config: ConnectionConfig): Promise<void>;
|
|
35
|
+
protected doDisconnect(): Promise<void>;
|
|
36
|
+
protected doQuery<T = unknown>(sql: string, params?: QueryParams, options?: QueryOptions): Promise<QueryResult<T>>;
|
|
37
|
+
beginTransaction(options?: TransactionOptions): Promise<Transaction>;
|
|
38
|
+
prepare<T = unknown>(sql: string, _name?: string): Promise<PreparedStatement<T>>;
|
|
39
|
+
getPoolStats(): PoolStats;
|
|
40
|
+
ping(): Promise<boolean>;
|
|
41
|
+
escape(value: unknown): string;
|
|
42
|
+
escapeIdentifier(identifier: string): string;
|
|
43
|
+
createQueryBuilder<T = unknown>(): MySQLQueryBuilder<T>;
|
|
44
|
+
private normalizeParams;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare class MySQLTransaction implements Transaction {
|
|
48
|
+
private connection;
|
|
49
|
+
private options?;
|
|
50
|
+
readonly id: string;
|
|
51
|
+
private _isActive;
|
|
52
|
+
private savepoints;
|
|
53
|
+
constructor(connection: mysql.PoolConnection, options?: TransactionOptions | undefined);
|
|
54
|
+
get isActive(): boolean;
|
|
55
|
+
begin(): Promise<void>;
|
|
56
|
+
commit(): Promise<void>;
|
|
57
|
+
rollback(): Promise<void>;
|
|
58
|
+
savepoint(name: string): Promise<void>;
|
|
59
|
+
releaseSavepoint(name: string): Promise<void>;
|
|
60
|
+
rollbackToSavepoint(name: string): Promise<void>;
|
|
61
|
+
getConnection(): mysql.PoolConnection;
|
|
62
|
+
query<T = unknown>(sql: string, params?: QueryParams, _options?: QueryOptions): Promise<QueryResult<T>>;
|
|
63
|
+
execute<T = unknown>(sql: string, params?: QueryParams, options?: QueryOptions): Promise<QueryResult<T>>;
|
|
64
|
+
private setIsolationLevel;
|
|
65
|
+
private mapIsolationLevel;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
declare class MySQLPreparedStatement<T = unknown> implements PreparedStatement<T> {
|
|
69
|
+
private connection;
|
|
70
|
+
private sql;
|
|
71
|
+
private released;
|
|
72
|
+
constructor(connection: mysql.PoolConnection, sql: string);
|
|
73
|
+
execute(params?: unknown[]): Promise<QueryResult<T>>;
|
|
74
|
+
release(): Promise<void>;
|
|
75
|
+
close(): Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
declare class MySQLConnectionPool {
|
|
79
|
+
private options;
|
|
80
|
+
private pool?;
|
|
81
|
+
constructor(options: mysql.ConnectionOptions);
|
|
82
|
+
initialize(): Promise<void>;
|
|
83
|
+
getConnection(): Promise<mysql.PoolConnection>;
|
|
84
|
+
end(): Promise<void>;
|
|
85
|
+
getStats(): PoolStats;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { MySQLAdapter, type MySQLAdapterOptions, MySQLConnectionPool, MySQLPreparedStatement, MySQLQueryBuilder, MySQLTransaction };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
import { QueryError, generateUUID, TransactionError, IsolationLevel, ConnectionError, BaseQueryBuilder, ValidationError, BaseAdapter, POOL_DEFAULTS, QueryTimeoutError } from '@db-bridge/core';
|
|
2
|
+
import * as mysql from 'mysql2/promise';
|
|
3
|
+
|
|
4
|
+
// src/adapter/mysql-adapter.ts
|
|
5
|
+
var MySQLPreparedStatement = class {
|
|
6
|
+
constructor(connection, sql) {
|
|
7
|
+
this.connection = connection;
|
|
8
|
+
this.sql = sql;
|
|
9
|
+
}
|
|
10
|
+
released = false;
|
|
11
|
+
async execute(params) {
|
|
12
|
+
if (this.released) {
|
|
13
|
+
throw new QueryError("Prepared statement has been released");
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const command = this.sql.trim().split(" ")[0]?.toUpperCase();
|
|
17
|
+
if (command === "INSERT" || command === "UPDATE" || command === "DELETE") {
|
|
18
|
+
const [result] = await this.connection.execute(
|
|
19
|
+
this.sql,
|
|
20
|
+
params || []
|
|
21
|
+
);
|
|
22
|
+
return {
|
|
23
|
+
rows: [],
|
|
24
|
+
rowCount: result.affectedRows || 0,
|
|
25
|
+
affectedRows: result.affectedRows || 0,
|
|
26
|
+
insertId: result.insertId,
|
|
27
|
+
fields: [],
|
|
28
|
+
command
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const [rows, fields] = await this.connection.execute(
|
|
32
|
+
this.sql,
|
|
33
|
+
params || []
|
|
34
|
+
);
|
|
35
|
+
return {
|
|
36
|
+
rows,
|
|
37
|
+
rowCount: Array.isArray(rows) ? rows.length : 0,
|
|
38
|
+
affectedRows: Array.isArray(rows) ? rows.length : 0,
|
|
39
|
+
fields: fields?.map((field) => ({
|
|
40
|
+
name: field.name,
|
|
41
|
+
type: field.type?.toString() || "unknown",
|
|
42
|
+
nullable: field.flags ? !(Number(field.flags) & 1) : true,
|
|
43
|
+
primaryKey: field.flags ? !!(Number(field.flags) & 2) : false,
|
|
44
|
+
autoIncrement: field.flags ? !!(Number(field.flags) & 512) : false,
|
|
45
|
+
defaultValue: field.default
|
|
46
|
+
})),
|
|
47
|
+
command
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throw new QueryError(
|
|
51
|
+
`Prepared statement execution failed: ${error.message}`,
|
|
52
|
+
this.sql,
|
|
53
|
+
params,
|
|
54
|
+
error
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async release() {
|
|
59
|
+
if (this.released) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
this.connection.release();
|
|
64
|
+
this.released = true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
throw new QueryError(
|
|
67
|
+
`Failed to release prepared statement: ${error.message}`,
|
|
68
|
+
this.sql,
|
|
69
|
+
void 0,
|
|
70
|
+
error
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Alias for release() - industry standard naming
|
|
76
|
+
*/
|
|
77
|
+
async close() {
|
|
78
|
+
return this.release();
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var MySQLTransaction = class {
|
|
82
|
+
constructor(connection, options) {
|
|
83
|
+
this.connection = connection;
|
|
84
|
+
this.options = options;
|
|
85
|
+
this.id = generateUUID();
|
|
86
|
+
}
|
|
87
|
+
id;
|
|
88
|
+
_isActive = false;
|
|
89
|
+
savepoints = /* @__PURE__ */ new Set();
|
|
90
|
+
get isActive() {
|
|
91
|
+
return this._isActive;
|
|
92
|
+
}
|
|
93
|
+
async begin() {
|
|
94
|
+
if (this._isActive) {
|
|
95
|
+
throw new TransactionError("Transaction already active", this.id);
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
if (this.options?.isolationLevel) {
|
|
99
|
+
await this.setIsolationLevel(this.options.isolationLevel);
|
|
100
|
+
}
|
|
101
|
+
await this.connection.beginTransaction();
|
|
102
|
+
this._isActive = true;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
throw new TransactionError("Failed to begin transaction", this.id, error);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async commit() {
|
|
108
|
+
if (!this._isActive) {
|
|
109
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
await this.connection.commit();
|
|
113
|
+
this._isActive = false;
|
|
114
|
+
this.savepoints.clear();
|
|
115
|
+
} catch (error) {
|
|
116
|
+
throw new TransactionError("Failed to commit transaction", this.id, error);
|
|
117
|
+
} finally {
|
|
118
|
+
this.connection.release();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async rollback() {
|
|
122
|
+
if (!this._isActive) {
|
|
123
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
await this.connection.rollback();
|
|
127
|
+
this._isActive = false;
|
|
128
|
+
this.savepoints.clear();
|
|
129
|
+
} catch (error) {
|
|
130
|
+
throw new TransactionError("Failed to rollback transaction", this.id, error);
|
|
131
|
+
} finally {
|
|
132
|
+
this.connection.release();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async savepoint(name) {
|
|
136
|
+
if (!this._isActive) {
|
|
137
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
138
|
+
}
|
|
139
|
+
if (this.savepoints.has(name)) {
|
|
140
|
+
throw new TransactionError(`Savepoint "${name}" already exists`, this.id);
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
await this.connection.query(`SAVEPOINT ${mysql.escapeId(name)}`);
|
|
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
|
+
await this.connection.query(`RELEASE SAVEPOINT ${mysql.escapeId(name)}`);
|
|
158
|
+
this.savepoints.delete(name);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
throw new TransactionError(`Failed to release savepoint "${name}"`, this.id, error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async rollbackToSavepoint(name) {
|
|
164
|
+
if (!this._isActive) {
|
|
165
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
166
|
+
}
|
|
167
|
+
if (!this.savepoints.has(name)) {
|
|
168
|
+
throw new TransactionError(`Savepoint "${name}" does not exist`, this.id);
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
await this.connection.query(`ROLLBACK TO SAVEPOINT ${mysql.escapeId(name)}`);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
throw new TransactionError(
|
|
174
|
+
`Failed to rollback to savepoint "${name}"`,
|
|
175
|
+
this.id,
|
|
176
|
+
error
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
getConnection() {
|
|
181
|
+
return this.connection;
|
|
182
|
+
}
|
|
183
|
+
async query(sql, params, _options) {
|
|
184
|
+
if (!this._isActive) {
|
|
185
|
+
throw new TransactionError("Transaction not active", this.id);
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const queryParams = Array.isArray(params) ? params : params ? Object.values(params) : [];
|
|
189
|
+
const command = sql.trim().split(" ")[0]?.toUpperCase();
|
|
190
|
+
if (command === "INSERT" || command === "UPDATE" || command === "DELETE") {
|
|
191
|
+
const [result2] = await this.connection.execute(sql, queryParams);
|
|
192
|
+
return {
|
|
193
|
+
rows: [],
|
|
194
|
+
rowCount: result2.affectedRows || 0,
|
|
195
|
+
affectedRows: result2.affectedRows || 0,
|
|
196
|
+
insertId: result2.insertId,
|
|
197
|
+
fields: [],
|
|
198
|
+
command
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const [rows, fields] = await this.connection.execute(sql, queryParams);
|
|
202
|
+
const result = {
|
|
203
|
+
rows,
|
|
204
|
+
rowCount: Array.isArray(rows) ? rows.length : 0,
|
|
205
|
+
affectedRows: Array.isArray(rows) ? rows.length : 0,
|
|
206
|
+
fields: fields?.map((field) => ({
|
|
207
|
+
name: field.name,
|
|
208
|
+
type: field.type?.toString() || "unknown",
|
|
209
|
+
nullable: field.flags ? !(Number(field.flags) & 1) : true,
|
|
210
|
+
primaryKey: field.flags ? !!(Number(field.flags) & 2) : false,
|
|
211
|
+
autoIncrement: field.flags ? !!(Number(field.flags) & 512) : false,
|
|
212
|
+
defaultValue: field.default
|
|
213
|
+
})),
|
|
214
|
+
command
|
|
215
|
+
};
|
|
216
|
+
return result;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
throw new TransactionError(
|
|
219
|
+
`Query failed in transaction: ${error.message}`,
|
|
220
|
+
this.id,
|
|
221
|
+
error
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Alias for query() to provide consistency with adapter's execute method
|
|
227
|
+
*/
|
|
228
|
+
async execute(sql, params, options) {
|
|
229
|
+
return this.query(sql, params, options);
|
|
230
|
+
}
|
|
231
|
+
async setIsolationLevel(level) {
|
|
232
|
+
const mysqlLevel = this.mapIsolationLevel(level);
|
|
233
|
+
await this.connection.query(`SET TRANSACTION ISOLATION LEVEL ${mysqlLevel}`);
|
|
234
|
+
}
|
|
235
|
+
mapIsolationLevel(level) {
|
|
236
|
+
switch (level) {
|
|
237
|
+
case IsolationLevel.READ_UNCOMMITTED: {
|
|
238
|
+
return "READ UNCOMMITTED";
|
|
239
|
+
}
|
|
240
|
+
case IsolationLevel.READ_COMMITTED: {
|
|
241
|
+
return "READ COMMITTED";
|
|
242
|
+
}
|
|
243
|
+
case IsolationLevel.REPEATABLE_READ: {
|
|
244
|
+
return "REPEATABLE READ";
|
|
245
|
+
}
|
|
246
|
+
case IsolationLevel.SERIALIZABLE: {
|
|
247
|
+
return "SERIALIZABLE";
|
|
248
|
+
}
|
|
249
|
+
default: {
|
|
250
|
+
throw new TransactionError(`Invalid isolation level: ${level}`, this.id);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
var MySQLConnectionPool = class {
|
|
256
|
+
constructor(options) {
|
|
257
|
+
this.options = options;
|
|
258
|
+
}
|
|
259
|
+
pool;
|
|
260
|
+
async initialize() {
|
|
261
|
+
try {
|
|
262
|
+
this.pool = mysql.createPool(this.options);
|
|
263
|
+
const connection = await this.pool.getConnection();
|
|
264
|
+
await connection.ping();
|
|
265
|
+
connection.release();
|
|
266
|
+
} catch (error) {
|
|
267
|
+
throw new ConnectionError("Failed to initialize MySQL connection pool", error);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
async getConnection() {
|
|
271
|
+
if (!this.pool) {
|
|
272
|
+
throw new ConnectionError("Connection pool not initialized");
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
return await this.pool.getConnection();
|
|
276
|
+
} catch (error) {
|
|
277
|
+
throw new ConnectionError("Failed to get connection from pool", error);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async end() {
|
|
281
|
+
if (this.pool) {
|
|
282
|
+
await this.pool.end();
|
|
283
|
+
this.pool = void 0;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
getStats() {
|
|
287
|
+
if (!this.pool) {
|
|
288
|
+
return {
|
|
289
|
+
total: 0,
|
|
290
|
+
idle: 0,
|
|
291
|
+
active: 0,
|
|
292
|
+
waiting: 0
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
const pool = this.pool;
|
|
296
|
+
const poolConfig = pool.config;
|
|
297
|
+
const connectionLimit = poolConfig?.connectionLimit || 10;
|
|
298
|
+
const allConnections = pool._allConnections || [];
|
|
299
|
+
const freeConnections = pool._freeConnections || [];
|
|
300
|
+
const connectionQueue = pool._connectionQueue || [];
|
|
301
|
+
return {
|
|
302
|
+
total: connectionLimit,
|
|
303
|
+
idle: freeConnections.length,
|
|
304
|
+
active: allConnections.length - freeConnections.length,
|
|
305
|
+
waiting: connectionQueue.length
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
var MySQLQueryBuilder = class extends BaseQueryBuilder {
|
|
310
|
+
buildSelectSQL() {
|
|
311
|
+
if (!this.fromTable) {
|
|
312
|
+
throw new ValidationError("FROM table is required for SELECT query");
|
|
313
|
+
}
|
|
314
|
+
const parts = [];
|
|
315
|
+
parts.push("SELECT");
|
|
316
|
+
parts.push(this.selectColumns.join(", "));
|
|
317
|
+
parts.push("FROM");
|
|
318
|
+
if (this.fromAlias) {
|
|
319
|
+
parts.push(
|
|
320
|
+
`${this.escapeIdentifierFn(this.fromTable)} AS ${this.escapeIdentifierFn(this.fromAlias)}`
|
|
321
|
+
);
|
|
322
|
+
} else {
|
|
323
|
+
parts.push(this.escapeIdentifierFn(this.fromTable));
|
|
324
|
+
}
|
|
325
|
+
this.joins.forEach((join) => {
|
|
326
|
+
parts.push(`${join.type} JOIN ${join.table} ON ${join.on}`);
|
|
327
|
+
});
|
|
328
|
+
if (this.whereClauses.length > 0) {
|
|
329
|
+
parts.push("WHERE");
|
|
330
|
+
const whereConditions = this.whereClauses.map((clause, index) => {
|
|
331
|
+
const prefix = index === 0 ? "" : clause.type;
|
|
332
|
+
return `${prefix} ${clause.condition}`.trim();
|
|
333
|
+
});
|
|
334
|
+
parts.push(whereConditions.join(" "));
|
|
335
|
+
}
|
|
336
|
+
if (this.groupByColumns.length > 0) {
|
|
337
|
+
parts.push("GROUP BY");
|
|
338
|
+
parts.push(this.groupByColumns.map((col) => this.escapeIdentifierFn(col)).join(", "));
|
|
339
|
+
}
|
|
340
|
+
if (this.havingClause) {
|
|
341
|
+
parts.push("HAVING");
|
|
342
|
+
parts.push(this.havingClause);
|
|
343
|
+
}
|
|
344
|
+
if (this.orderByColumns.length > 0) {
|
|
345
|
+
parts.push("ORDER BY");
|
|
346
|
+
const orderClauses = this.orderByColumns.map(
|
|
347
|
+
({ column, direction }) => `${this.escapeIdentifierFn(column)} ${direction}`
|
|
348
|
+
);
|
|
349
|
+
parts.push(orderClauses.join(", "));
|
|
350
|
+
}
|
|
351
|
+
if (this.limitValue !== void 0) {
|
|
352
|
+
parts.push(`LIMIT ${this.limitValue}`);
|
|
353
|
+
}
|
|
354
|
+
if (this.offsetValue !== void 0) {
|
|
355
|
+
parts.push(`OFFSET ${this.offsetValue}`);
|
|
356
|
+
}
|
|
357
|
+
return { sql: parts.join(" "), bindings: this.bindings };
|
|
358
|
+
}
|
|
359
|
+
buildInsertSQL() {
|
|
360
|
+
if (!this.insertTable || !this.insertData) {
|
|
361
|
+
throw new ValidationError("Table and data are required for INSERT query");
|
|
362
|
+
}
|
|
363
|
+
const dataArray = Array.isArray(this.insertData) ? this.insertData : [this.insertData];
|
|
364
|
+
if (dataArray.length === 0) {
|
|
365
|
+
throw new ValidationError("Cannot insert empty data");
|
|
366
|
+
}
|
|
367
|
+
const firstRow = dataArray[0];
|
|
368
|
+
const columns = Object.keys(firstRow);
|
|
369
|
+
const bindings = [];
|
|
370
|
+
const valueRows = dataArray.map((row) => {
|
|
371
|
+
const values = columns.map((col) => {
|
|
372
|
+
bindings.push(row[col]);
|
|
373
|
+
return "?";
|
|
374
|
+
});
|
|
375
|
+
return `(${values.join(", ")})`;
|
|
376
|
+
});
|
|
377
|
+
const sql = `INSERT INTO ${this.escapeIdentifierFn(this.insertTable)} (${columns.map((col) => this.escapeIdentifierFn(col)).join(", ")}) VALUES ${valueRows.join(", ")}`;
|
|
378
|
+
return { sql, bindings };
|
|
379
|
+
}
|
|
380
|
+
buildUpdateSQL() {
|
|
381
|
+
if (!this.updateTable || !this.updateData) {
|
|
382
|
+
throw new ValidationError("Table and data are required for UPDATE query");
|
|
383
|
+
}
|
|
384
|
+
const parts = [];
|
|
385
|
+
const bindings = [];
|
|
386
|
+
parts.push("UPDATE");
|
|
387
|
+
parts.push(this.escapeIdentifierFn(this.updateTable));
|
|
388
|
+
parts.push("SET");
|
|
389
|
+
const setClauses = Object.entries(this.updateData).map(([key, value]) => {
|
|
390
|
+
bindings.push(value);
|
|
391
|
+
return `${this.escapeIdentifierFn(key)} = ?`;
|
|
392
|
+
});
|
|
393
|
+
parts.push(setClauses.join(", "));
|
|
394
|
+
if (this.whereClauses.length > 0) {
|
|
395
|
+
parts.push("WHERE");
|
|
396
|
+
const whereConditions = this.whereClauses.map((clause, index) => {
|
|
397
|
+
const prefix = index === 0 ? "" : clause.type;
|
|
398
|
+
return `${prefix} ${clause.condition}`.trim();
|
|
399
|
+
});
|
|
400
|
+
parts.push(whereConditions.join(" "));
|
|
401
|
+
bindings.push(...this.bindings);
|
|
402
|
+
}
|
|
403
|
+
return { sql: parts.join(" "), bindings };
|
|
404
|
+
}
|
|
405
|
+
buildDeleteSQL() {
|
|
406
|
+
if (!this.deleteTable) {
|
|
407
|
+
throw new ValidationError("Table is required for DELETE query");
|
|
408
|
+
}
|
|
409
|
+
const parts = [];
|
|
410
|
+
const bindings = [];
|
|
411
|
+
parts.push("DELETE FROM");
|
|
412
|
+
parts.push(this.escapeIdentifierFn(this.deleteTable));
|
|
413
|
+
if (this.whereClauses.length > 0) {
|
|
414
|
+
parts.push("WHERE");
|
|
415
|
+
const whereConditions = this.whereClauses.map((clause, index) => {
|
|
416
|
+
const prefix = index === 0 ? "" : clause.type;
|
|
417
|
+
return `${prefix} ${clause.condition}`.trim();
|
|
418
|
+
});
|
|
419
|
+
parts.push(whereConditions.join(" "));
|
|
420
|
+
bindings.push(...this.bindings);
|
|
421
|
+
}
|
|
422
|
+
return { sql: parts.join(" "), bindings };
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
// src/adapter/mysql-adapter.ts
|
|
427
|
+
var MySQLAdapter = class extends BaseAdapter {
|
|
428
|
+
name = "MySQL";
|
|
429
|
+
version = "2.0.0";
|
|
430
|
+
pool;
|
|
431
|
+
mysql2Options;
|
|
432
|
+
poolConfig = {
|
|
433
|
+
min: POOL_DEFAULTS.min,
|
|
434
|
+
max: POOL_DEFAULTS.max,
|
|
435
|
+
acquireTimeout: POOL_DEFAULTS.acquireTimeout,
|
|
436
|
+
idleTimeout: POOL_DEFAULTS.idleTimeout,
|
|
437
|
+
queueLimit: POOL_DEFAULTS.queueLimit,
|
|
438
|
+
queryTimeout: POOL_DEFAULTS.queryTimeout
|
|
439
|
+
};
|
|
440
|
+
constructor(options = {}) {
|
|
441
|
+
super(options);
|
|
442
|
+
this.mysql2Options = options.mysql2Options;
|
|
443
|
+
}
|
|
444
|
+
async doConnect(config) {
|
|
445
|
+
this.poolConfig = {
|
|
446
|
+
min: config.pool?.min ?? POOL_DEFAULTS.min,
|
|
447
|
+
max: config.pool?.max ?? config.poolSize ?? POOL_DEFAULTS.max,
|
|
448
|
+
acquireTimeout: config.pool?.acquireTimeout ?? POOL_DEFAULTS.acquireTimeout,
|
|
449
|
+
idleTimeout: config.pool?.idleTimeout ?? POOL_DEFAULTS.idleTimeout,
|
|
450
|
+
queueLimit: config.pool?.queueLimit ?? POOL_DEFAULTS.queueLimit,
|
|
451
|
+
queryTimeout: config.pool?.queryTimeout ?? POOL_DEFAULTS.queryTimeout
|
|
452
|
+
};
|
|
453
|
+
const connectionOptions = {
|
|
454
|
+
host: config.host,
|
|
455
|
+
port: config.port || 3306,
|
|
456
|
+
user: config.user,
|
|
457
|
+
password: config.password,
|
|
458
|
+
database: config.database,
|
|
459
|
+
// Pool configuration with production-ready defaults
|
|
460
|
+
connectionLimit: this.poolConfig.max,
|
|
461
|
+
waitForConnections: true,
|
|
462
|
+
queueLimit: this.poolConfig.queueLimit,
|
|
463
|
+
connectTimeout: config.connectionTimeout || 1e4,
|
|
464
|
+
idleTimeout: this.poolConfig.idleTimeout,
|
|
465
|
+
// Additional pool options
|
|
466
|
+
...config.pool?.enableKeepAlive !== false && {
|
|
467
|
+
enableKeepAlive: true,
|
|
468
|
+
keepAliveInitialDelay: config.pool?.keepAliveInitialDelay || 1e4
|
|
469
|
+
},
|
|
470
|
+
...this.mysql2Options
|
|
471
|
+
};
|
|
472
|
+
if (config.ssl) {
|
|
473
|
+
connectionOptions.ssl = typeof config.ssl === "boolean" ? {} : config.ssl;
|
|
474
|
+
}
|
|
475
|
+
this.pool = new MySQLConnectionPool(connectionOptions);
|
|
476
|
+
await this.pool.initialize();
|
|
477
|
+
this.logger?.info("Connected to MySQL database", {
|
|
478
|
+
database: config.database,
|
|
479
|
+
poolSize: this.poolConfig.max,
|
|
480
|
+
queueLimit: this.poolConfig.queueLimit,
|
|
481
|
+
queryTimeout: this.poolConfig.queryTimeout
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
async doDisconnect() {
|
|
485
|
+
if (this.pool) {
|
|
486
|
+
await this.pool.end();
|
|
487
|
+
this.pool = void 0;
|
|
488
|
+
this.logger?.info("Disconnected from MySQL database");
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async doQuery(sql, params, options) {
|
|
492
|
+
if (!this.pool) {
|
|
493
|
+
throw new ConnectionError("Database pool not initialized");
|
|
494
|
+
}
|
|
495
|
+
const connection = options?.transaction ? options.transaction.getConnection() : await this.pool.getConnection();
|
|
496
|
+
try {
|
|
497
|
+
const queryParams = this.normalizeParams(params);
|
|
498
|
+
const command = sql.trim().split(" ")[0]?.toUpperCase();
|
|
499
|
+
const timeout = options?.timeout ?? this.poolConfig.queryTimeout;
|
|
500
|
+
const executeWithTimeout = async (executor) => {
|
|
501
|
+
if (!timeout) {
|
|
502
|
+
return executor();
|
|
503
|
+
}
|
|
504
|
+
return Promise.race([
|
|
505
|
+
executor(),
|
|
506
|
+
new Promise(
|
|
507
|
+
(_, reject) => setTimeout(() => reject(new QueryTimeoutError(sql.slice(0, 100), timeout)), timeout)
|
|
508
|
+
)
|
|
509
|
+
]);
|
|
510
|
+
};
|
|
511
|
+
if (command === "INSERT" || command === "UPDATE" || command === "DELETE") {
|
|
512
|
+
const [result] = await executeWithTimeout(
|
|
513
|
+
() => connection.execute(sql, queryParams)
|
|
514
|
+
);
|
|
515
|
+
return {
|
|
516
|
+
rows: [],
|
|
517
|
+
rowCount: result.affectedRows || 0,
|
|
518
|
+
affectedRows: result.affectedRows || 0,
|
|
519
|
+
insertId: result.insertId,
|
|
520
|
+
fields: [],
|
|
521
|
+
command
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
const [rows, fields] = await executeWithTimeout(
|
|
525
|
+
() => connection.execute(sql, queryParams)
|
|
526
|
+
);
|
|
527
|
+
return {
|
|
528
|
+
rows,
|
|
529
|
+
rowCount: Array.isArray(rows) ? rows.length : 0,
|
|
530
|
+
affectedRows: Array.isArray(rows) ? rows.length : 0,
|
|
531
|
+
fields: fields?.map((field) => ({
|
|
532
|
+
name: field.name,
|
|
533
|
+
type: field.type?.toString() || "unknown",
|
|
534
|
+
nullable: field.flags ? !(Number(field.flags) & 1) : true,
|
|
535
|
+
primaryKey: field.flags ? !!(Number(field.flags) & 2) : false,
|
|
536
|
+
autoIncrement: field.flags ? !!(Number(field.flags) & 512) : false,
|
|
537
|
+
defaultValue: field.default
|
|
538
|
+
})),
|
|
539
|
+
command
|
|
540
|
+
};
|
|
541
|
+
} finally {
|
|
542
|
+
if (!options?.transaction) {
|
|
543
|
+
connection.release();
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async beginTransaction(options) {
|
|
548
|
+
if (!this.pool) {
|
|
549
|
+
throw new ConnectionError("Database pool not initialized");
|
|
550
|
+
}
|
|
551
|
+
const connection = await this.pool.getConnection();
|
|
552
|
+
const transaction = new MySQLTransaction(connection, options);
|
|
553
|
+
try {
|
|
554
|
+
await transaction.begin();
|
|
555
|
+
this.logger?.debug("Transaction started", { id: transaction.id });
|
|
556
|
+
return transaction;
|
|
557
|
+
} catch (error) {
|
|
558
|
+
connection.release();
|
|
559
|
+
throw new TransactionError("Failed to begin transaction", void 0, error);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
async prepare(sql, _name) {
|
|
563
|
+
if (!this.pool) {
|
|
564
|
+
throw new ConnectionError("Database pool not initialized");
|
|
565
|
+
}
|
|
566
|
+
const connection = await this.pool.getConnection();
|
|
567
|
+
return new MySQLPreparedStatement(connection, sql);
|
|
568
|
+
}
|
|
569
|
+
getPoolStats() {
|
|
570
|
+
if (!this.pool) {
|
|
571
|
+
return {
|
|
572
|
+
total: 0,
|
|
573
|
+
idle: 0,
|
|
574
|
+
active: 0,
|
|
575
|
+
waiting: 0
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
return this.pool.getStats();
|
|
579
|
+
}
|
|
580
|
+
async ping() {
|
|
581
|
+
if (!this.pool) {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
try {
|
|
585
|
+
const connection = await this.pool.getConnection();
|
|
586
|
+
try {
|
|
587
|
+
await connection.ping();
|
|
588
|
+
return true;
|
|
589
|
+
} finally {
|
|
590
|
+
connection.release();
|
|
591
|
+
}
|
|
592
|
+
} catch {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
escape(value) {
|
|
597
|
+
return mysql.escape(value);
|
|
598
|
+
}
|
|
599
|
+
escapeIdentifier(identifier) {
|
|
600
|
+
return mysql.escapeId(identifier);
|
|
601
|
+
}
|
|
602
|
+
createQueryBuilder() {
|
|
603
|
+
return new MySQLQueryBuilder({
|
|
604
|
+
adapter: this,
|
|
605
|
+
escapeIdentifier: (id) => this.escapeIdentifier(id),
|
|
606
|
+
parameterPlaceholder: () => "?",
|
|
607
|
+
crypto: this.crypto
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
normalizeParams(params) {
|
|
611
|
+
if (!params) {
|
|
612
|
+
return [];
|
|
613
|
+
}
|
|
614
|
+
if (Array.isArray(params)) {
|
|
615
|
+
return params;
|
|
616
|
+
}
|
|
617
|
+
return Object.values(params);
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
export { MySQLAdapter, MySQLConnectionPool, MySQLPreparedStatement, MySQLQueryBuilder, MySQLTransaction };
|
|
622
|
+
//# sourceMappingURL=index.js.map
|
|
623
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapter/mysql-prepared-statement.ts","../src/adapter/mysql-transaction.ts","../src/pool/connection-pool.ts","../src/query-builder/mysql-query-builder.ts","../src/adapter/mysql-adapter.ts"],"names":["result","mysql2","ConnectionError","TransactionError","mysql3"],"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 * as mysql from 'mysql2/promise';\n\nexport class MySQLPreparedStatement<T = unknown> implements PreparedStatement<T> {\n private released = false;\n\n constructor(\n private connection: mysql.PoolConnection,\n private sql: 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 const command = this.sql.trim().split(' ')[0]?.toUpperCase();\n\n // For INSERT/UPDATE/DELETE, we get ResultSetHeader instead of RowDataPacket[]\n if (command === 'INSERT' || command === 'UPDATE' || command === 'DELETE') {\n const [result] = await this.connection.execute<mysql.ResultSetHeader>(\n this.sql,\n params || [],\n );\n return {\n rows: [] as T[],\n rowCount: result.affectedRows || 0,\n affectedRows: result.affectedRows || 0,\n insertId: result.insertId,\n fields: [],\n command,\n };\n }\n\n const [rows, fields] = await this.connection.execute<mysql.RowDataPacket[]>(\n this.sql,\n params || [],\n );\n\n return {\n rows: rows as T[],\n rowCount: Array.isArray(rows) ? rows.length : 0,\n affectedRows: Array.isArray(rows) ? rows.length : 0,\n fields: fields?.map((field) => ({\n name: field.name,\n type: field.type?.toString() || 'unknown',\n nullable: field.flags ? !(Number(field.flags) & 1) : true,\n primaryKey: field.flags ? !!(Number(field.flags) & 2) : false,\n autoIncrement: field.flags ? !!(Number(field.flags) & 512) : false,\n defaultValue: field.default,\n })),\n 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 // MySQL doesn't have unprepare, just release connection\n this.connection.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 { IsolationLevel, TransactionError, generateUUID } from '@db-bridge/core';\nimport * as mysql from 'mysql2/promise';\n\nimport type {\n Transaction,\n TransactionOptions,\n QueryParams,\n QueryOptions,\n QueryResult,\n} from '@db-bridge/core';\n\nexport class MySQLTransaction implements Transaction {\n readonly id: string;\n private _isActive = false;\n private savepoints: Set<string> = new Set();\n\n constructor(\n private connection: mysql.PoolConnection,\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 if (this.options?.isolationLevel) {\n await this.setIsolationLevel(this.options.isolationLevel);\n }\n\n await this.connection.beginTransaction();\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.connection.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.connection.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.connection.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.connection.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 await this.connection.query(`SAVEPOINT ${mysql.escapeId(name)}`);\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 await this.connection.query(`RELEASE SAVEPOINT ${mysql.escapeId(name)}`);\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 await this.connection.query(`ROLLBACK TO SAVEPOINT ${mysql.escapeId(name)}`);\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 getConnection(): mysql.PoolConnection {\n return this.connection;\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 command = sql.trim().split(' ')[0]?.toUpperCase();\n\n // For INSERT/UPDATE/DELETE, we get ResultSetHeader instead of RowDataPacket[]\n if (command === 'INSERT' || command === 'UPDATE' || command === 'DELETE') {\n const [result] = await this.connection.execute<mysql.ResultSetHeader>(sql, queryParams);\n return {\n rows: [] as T[],\n rowCount: result.affectedRows || 0,\n affectedRows: result.affectedRows || 0,\n insertId: result.insertId,\n fields: [],\n command,\n };\n }\n\n const [rows, fields] = await this.connection.execute<mysql.RowDataPacket[]>(sql, queryParams);\n\n const result: QueryResult<T> = {\n rows: rows as T[],\n rowCount: Array.isArray(rows) ? rows.length : 0,\n affectedRows: Array.isArray(rows) ? rows.length : 0,\n fields: fields?.map((field) => ({\n name: field.name,\n type: field.type?.toString() || 'unknown',\n nullable: field.flags ? !(Number(field.flags) & 1) : true,\n primaryKey: field.flags ? !!(Number(field.flags) & 2) : false,\n autoIncrement: field.flags ? !!(Number(field.flags) & 512) : false,\n defaultValue: field.default,\n })),\n command,\n };\n\n return result;\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 private async setIsolationLevel(level: IsolationLevel): Promise<void> {\n const mysqlLevel = this.mapIsolationLevel(level);\n await this.connection.query(`SET TRANSACTION ISOLATION LEVEL ${mysqlLevel}`);\n }\n\n private mapIsolationLevel(level: IsolationLevel): string {\n switch (level) {\n case IsolationLevel.READ_UNCOMMITTED: {\n return 'READ UNCOMMITTED';\n }\n case IsolationLevel.READ_COMMITTED: {\n return 'READ COMMITTED';\n }\n case IsolationLevel.REPEATABLE_READ: {\n return 'REPEATABLE READ';\n }\n case IsolationLevel.SERIALIZABLE: {\n return 'SERIALIZABLE';\n }\n default: {\n throw new TransactionError(`Invalid isolation level: ${level}`, this.id);\n }\n }\n }\n}\n","import { ConnectionError } from '@db-bridge/core';\nimport * as mysql from 'mysql2/promise';\n\nimport type { PoolStats } from '@db-bridge/core';\n\nexport class MySQLConnectionPool {\n private pool?: mysql.Pool;\n\n constructor(private options: mysql.ConnectionOptions) {}\n\n async initialize(): Promise<void> {\n try {\n this.pool = mysql.createPool(this.options);\n\n const connection = await this.pool.getConnection();\n await connection.ping();\n connection.release();\n } catch (error) {\n throw new ConnectionError('Failed to initialize MySQL connection pool', error as Error);\n }\n }\n\n async getConnection(): Promise<mysql.PoolConnection> {\n if (!this.pool) {\n throw new ConnectionError('Connection pool not initialized');\n }\n\n try {\n return await this.pool.getConnection();\n } catch (error) {\n throw new ConnectionError('Failed to get connection 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 // mysql2 doesn't expose pool stats directly, so we use internal properties\n // These may change in future versions of mysql2\n const pool = this.pool as any;\n const poolConfig = pool.config;\n\n // Get connection limit from config\n const connectionLimit = poolConfig?.connectionLimit || 10;\n\n // Try to access internal pool state\n const allConnections = pool._allConnections || [];\n const freeConnections = pool._freeConnections || [];\n const connectionQueue = pool._connectionQueue || [];\n\n return {\n total: connectionLimit,\n idle: freeConnections.length,\n active: allConnections.length - freeConnections.length,\n waiting: connectionQueue.length,\n };\n }\n}\n","import { BaseQueryBuilder, ValidationError } from '@db-bridge/core';\n\nexport class MySQLQueryBuilder<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\n const valueRows = dataArray.map((row) => {\n const values = columns.map((col) => {\n bindings.push(row[col]);\n return '?';\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\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)} = ?`;\n });\n parts.push(setClauses.join(', '));\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 bindings.push(...this.bindings);\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\n parts.push('DELETE FROM');\n parts.push(this.escapeIdentifierFn(this.deleteTable));\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 bindings.push(...this.bindings);\n }\n\n return { sql: parts.join(' '), bindings };\n }\n}\n","import {\n BaseAdapter,\n ConnectionError,\n TransactionError,\n QueryTimeoutError,\n POOL_DEFAULTS,\n} from '@db-bridge/core';\nimport * as mysql from 'mysql2/promise';\n\nimport { MySQLPreparedStatement } from './mysql-prepared-statement';\nimport { MySQLTransaction } from './mysql-transaction';\nimport { MySQLConnectionPool } from '../pool/connection-pool';\nimport { MySQLQueryBuilder } from '../query-builder/mysql-query-builder';\n\nimport type {\n BaseAdapterOptions,\n ConnectionConfig,\n PoolStats,\n PreparedStatement,\n QueryOptions,\n QueryParams,\n QueryResult,\n Transaction,\n TransactionOptions,\n} from '@db-bridge/core';\n\nexport interface MySQLAdapterOptions extends BaseAdapterOptions {\n mysql2Options?: mysql.ConnectionOptions;\n}\n\nexport class MySQLAdapter extends BaseAdapter {\n readonly name = 'MySQL';\n readonly version = '2.0.0';\n\n private pool?: MySQLConnectionPool;\n private readonly mysql2Options?: mysql.ConnectionOptions;\n private poolConfig: {\n min: number;\n max: number;\n acquireTimeout: number;\n idleTimeout: number;\n queueLimit: number;\n queryTimeout: number;\n } = {\n min: POOL_DEFAULTS.min,\n max: POOL_DEFAULTS.max,\n acquireTimeout: POOL_DEFAULTS.acquireTimeout,\n idleTimeout: POOL_DEFAULTS.idleTimeout,\n queueLimit: POOL_DEFAULTS.queueLimit,\n queryTimeout: POOL_DEFAULTS.queryTimeout,\n };\n\n constructor(options: MySQLAdapterOptions = {}) {\n super(options);\n this.mysql2Options = options.mysql2Options;\n }\n\n protected async doConnect(config: ConnectionConfig): Promise<void> {\n // Merge pool config with defaults\n this.poolConfig = {\n min: config.pool?.min ?? POOL_DEFAULTS.min,\n max: config.pool?.max ?? config.poolSize ?? POOL_DEFAULTS.max,\n acquireTimeout: config.pool?.acquireTimeout ?? POOL_DEFAULTS.acquireTimeout,\n idleTimeout: config.pool?.idleTimeout ?? POOL_DEFAULTS.idleTimeout,\n queueLimit: config.pool?.queueLimit ?? POOL_DEFAULTS.queueLimit,\n queryTimeout: config.pool?.queryTimeout ?? POOL_DEFAULTS.queryTimeout,\n };\n\n const connectionOptions: mysql.ConnectionOptions = {\n host: config.host,\n port: config.port || 3306,\n user: config.user,\n password: config.password,\n database: config.database,\n\n // Pool configuration with production-ready defaults\n connectionLimit: this.poolConfig.max,\n waitForConnections: true,\n queueLimit: this.poolConfig.queueLimit,\n connectTimeout: config.connectionTimeout || 10000,\n idleTimeout: this.poolConfig.idleTimeout,\n\n // Additional pool options\n ...(config.pool?.enableKeepAlive !== false && {\n enableKeepAlive: true,\n keepAliveInitialDelay: config.pool?.keepAliveInitialDelay || 10000,\n }),\n\n ...this.mysql2Options,\n };\n\n if (config.ssl) {\n connectionOptions.ssl = typeof config.ssl === 'boolean' ? {} : config.ssl;\n }\n\n this.pool = new MySQLConnectionPool(connectionOptions);\n await this.pool.initialize();\n\n this.logger?.info('Connected to MySQL database', {\n database: config.database,\n poolSize: this.poolConfig.max,\n queueLimit: this.poolConfig.queueLimit,\n queryTimeout: this.poolConfig.queryTimeout,\n });\n }\n\n protected async doDisconnect(): Promise<void> {\n if (this.pool) {\n await this.pool.end();\n this.pool = undefined;\n this.logger?.info('Disconnected from MySQL 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 connection = options?.transaction\n ? (options.transaction as MySQLTransaction).getConnection()\n : await this.pool.getConnection();\n\n try {\n const queryParams = this.normalizeParams(params);\n const command = sql.trim().split(' ')[0]?.toUpperCase();\n const timeout = options?.timeout ?? this.poolConfig.queryTimeout;\n\n // Execute with timeout\n const executeWithTimeout = async <R>(executor: () => Promise<R>): Promise<R> => {\n if (!timeout) {\n return executor();\n }\n\n return Promise.race([\n executor(),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new QueryTimeoutError(sql.slice(0, 100), timeout)), timeout),\n ),\n ]);\n };\n\n // For INSERT/UPDATE/DELETE, we get ResultSetHeader instead of RowDataPacket[]\n if (command === 'INSERT' || command === 'UPDATE' || command === 'DELETE') {\n const [result] = await executeWithTimeout(() =>\n connection.execute<mysql.ResultSetHeader>(sql, queryParams),\n );\n return {\n rows: [] as T[],\n rowCount: result.affectedRows || 0,\n affectedRows: result.affectedRows || 0,\n insertId: result.insertId,\n fields: [],\n command,\n };\n }\n\n // For SELECT and other queries\n const [rows, fields] = await executeWithTimeout(() =>\n connection.execute<mysql.RowDataPacket[]>(sql, queryParams),\n );\n\n return {\n rows: rows as T[],\n rowCount: Array.isArray(rows) ? rows.length : 0,\n affectedRows: Array.isArray(rows) ? rows.length : 0,\n fields: fields?.map((field) => ({\n name: field.name,\n type: field.type?.toString() || 'unknown',\n nullable: field.flags ? !(Number(field.flags) & 1) : true,\n primaryKey: field.flags ? !!(Number(field.flags) & 2) : false,\n autoIncrement: field.flags ? !!(Number(field.flags) & 512) : false,\n defaultValue: field.default,\n })),\n command,\n };\n } finally {\n if (!options?.transaction) {\n connection.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 connection = await this.pool.getConnection();\n const transaction = new MySQLTransaction(connection, options);\n\n try {\n await transaction.begin();\n this.logger?.debug('Transaction started', { id: transaction.id });\n return transaction;\n } catch (error) {\n connection.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 connection = await this.pool.getConnection();\n return new MySQLPreparedStatement<T>(connection, sql);\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 connection = await this.pool.getConnection();\n try {\n await connection.ping();\n return true;\n } finally {\n connection.release();\n }\n } catch {\n return false;\n }\n }\n\n override escape(value: unknown): string {\n return mysql.escape(value);\n }\n\n override escapeIdentifier(identifier: string): string {\n return mysql.escapeId(identifier);\n }\n\n createQueryBuilder<T = unknown>(): MySQLQueryBuilder<T> {\n return new MySQLQueryBuilder<T>({\n adapter: this,\n escapeIdentifier: (id) => this.escapeIdentifier(id),\n parameterPlaceholder: () => '?',\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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "module",
|
|
3
|
+
"name": "@db-bridge/mysql",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "MySQL 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
|
+
"mysql2": "^3.9.0 || ^4.0.0"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"mysql2": {
|
|
35
|
+
"optional": false
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.0.0",
|
|
40
|
+
"@vitest/coverage-v8": "^3.0.0",
|
|
41
|
+
"mysql2": "^3.16.0",
|
|
42
|
+
"tsup": "^8.0.1",
|
|
43
|
+
"typescript": "^5.3.0",
|
|
44
|
+
"vitest": "^3.0.0"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=16.0.0"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"gitHead": "7aaca822d0d43120f453562de592e48e7d3e8d32"
|
|
54
|
+
}
|