bunql 1.0.0 → 1.0.1-dev.2
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/README.md +66 -1
- package/package.json +1 -1
- package/src/index.test.ts +31 -0
- package/src/index.ts +80 -8
package/README.md
CHANGED
|
@@ -49,6 +49,35 @@ const users = await db.select('*').from('users').execute();
|
|
|
49
49
|
|
|
50
50
|
This makes the API more concise and intuitive while maintaining backward compatibility.
|
|
51
51
|
|
|
52
|
+
## Count Functionality
|
|
53
|
+
|
|
54
|
+
BunQL provides `.count()` methods on all query types to get the number of records:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Count all records in a table
|
|
58
|
+
const totalUsers = await db.select('*').from('users').count();
|
|
59
|
+
|
|
60
|
+
// Count with WHERE conditions
|
|
61
|
+
const activeUsers = await db.select('*')
|
|
62
|
+
.from('users')
|
|
63
|
+
.where('active', '=', true)
|
|
64
|
+
.count();
|
|
65
|
+
|
|
66
|
+
// Count records that would be affected by an update
|
|
67
|
+
const usersToUpdate = await db.update('users')
|
|
68
|
+
.set({ active: false })
|
|
69
|
+
.where('last_login', '<', new Date('2023-01-01'))
|
|
70
|
+
.count();
|
|
71
|
+
|
|
72
|
+
// Count records that would be deleted
|
|
73
|
+
const usersToDelete = await db.delete('users')
|
|
74
|
+
.where('active', '=', false)
|
|
75
|
+
.count();
|
|
76
|
+
|
|
77
|
+
// Count total records in table (for insert queries)
|
|
78
|
+
const totalRecords = await db.insert('users').values({ name: 'Test' }).count();
|
|
79
|
+
```
|
|
80
|
+
|
|
52
81
|
## Usage Examples
|
|
53
82
|
|
|
54
83
|
### Select Queries
|
|
@@ -165,6 +194,37 @@ const user = await db.get('SELECT * FROM users WHERE id = ?', [1]);
|
|
|
165
194
|
|
|
166
195
|
### Transactions
|
|
167
196
|
|
|
197
|
+
BunQL provides two ways to handle transactions:
|
|
198
|
+
|
|
199
|
+
#### Method 1: Using `db.transaction()` (Recommended)
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
// Clean transaction API with automatic rollback on error
|
|
203
|
+
const result = await db.transaction(async (trx) => {
|
|
204
|
+
// Insert user
|
|
205
|
+
const userResult = await trx.insert('users')
|
|
206
|
+
.values({ name: 'John', email: 'john@example.com' });
|
|
207
|
+
|
|
208
|
+
// Insert user profile
|
|
209
|
+
await trx.insert('user_profiles')
|
|
210
|
+
.values({
|
|
211
|
+
user_id: userResult.lastInsertRowid,
|
|
212
|
+
bio: 'Software developer'
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Update user status
|
|
216
|
+
await trx.update('users')
|
|
217
|
+
.set({ active: true })
|
|
218
|
+
.where('id', '=', userResult.lastInsertRowid);
|
|
219
|
+
|
|
220
|
+
return userResult.lastInsertRowid;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
console.log('Transaction completed, user ID:', result);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Method 2: Using `db.begin()` (Legacy)
|
|
227
|
+
|
|
168
228
|
```typescript
|
|
169
229
|
// Transaction with automatic rollback on error
|
|
170
230
|
const result = await db.begin(async (tx) => {
|
|
@@ -392,7 +452,8 @@ Main class for building and executing queries.
|
|
|
392
452
|
- `run(query, params?)`: Execute raw SQL
|
|
393
453
|
- `all(query, params?)`: Execute raw SQL and return all results
|
|
394
454
|
- `get(query, params?)`: Execute raw SQL and return first result
|
|
395
|
-
- `begin(callback)`: Execute queries in a transaction
|
|
455
|
+
- `begin(callback)`: Execute queries in a transaction (legacy)
|
|
456
|
+
- `transaction(callback)`: Execute queries in a transaction (recommended)
|
|
396
457
|
|
|
397
458
|
### SelectQuery
|
|
398
459
|
|
|
@@ -410,6 +471,7 @@ Methods for building SELECT queries.
|
|
|
410
471
|
- `execute()`: Execute the query and return results
|
|
411
472
|
- `first()`: Execute the query and return first result
|
|
412
473
|
- `all()`: Alias for `execute()`
|
|
474
|
+
- `count()`: Execute and return count of matching records
|
|
413
475
|
|
|
414
476
|
### InsertQuery
|
|
415
477
|
|
|
@@ -419,6 +481,7 @@ Methods for building INSERT queries.
|
|
|
419
481
|
|
|
420
482
|
- `values(data)`: Specify values to insert (object or array)
|
|
421
483
|
- `execute()`: Execute the insert query
|
|
484
|
+
- `count()`: Return count of total records in table
|
|
422
485
|
|
|
423
486
|
### UpdateQuery
|
|
424
487
|
|
|
@@ -429,6 +492,7 @@ Methods for building UPDATE queries.
|
|
|
429
492
|
- `set(data)`: Specify values to update
|
|
430
493
|
- `where(column, operator, value)`: Add WHERE condition
|
|
431
494
|
- `execute()`: Execute the update query
|
|
495
|
+
- `count()`: Return count of records that would be affected
|
|
432
496
|
|
|
433
497
|
### DeleteQuery
|
|
434
498
|
|
|
@@ -438,6 +502,7 @@ Methods for building DELETE queries.
|
|
|
438
502
|
|
|
439
503
|
- `where(column, operator, value)`: Add WHERE condition
|
|
440
504
|
- `execute()`: Execute the delete query
|
|
505
|
+
- `count()`: Return count of records that would be deleted
|
|
441
506
|
|
|
442
507
|
## Error Handling
|
|
443
508
|
|
package/package.json
CHANGED
package/src/index.test.ts
CHANGED
|
@@ -243,6 +243,33 @@ describe('BunQL', () => {
|
|
|
243
243
|
});
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
+
describe('Count Functionality', () => {
|
|
247
|
+
it('should have count method on select queries', () => {
|
|
248
|
+
const query = db.select('*').from('users');
|
|
249
|
+
expect(typeof query.count).toBe('function');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should have count method on update queries', () => {
|
|
253
|
+
const query = db.update('users').set({ name: 'Test' });
|
|
254
|
+
expect(typeof query.count).toBe('function');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should have count method on insert queries', () => {
|
|
258
|
+
const query = db.insert('users').values({ name: 'Test' });
|
|
259
|
+
expect(typeof query.count).toBe('function');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should have count method on delete queries', () => {
|
|
263
|
+
const query = db.delete('users').where('id', '=', 1);
|
|
264
|
+
expect(typeof query.count).toBe('function');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should throw error when calling count on select without table', async () => {
|
|
268
|
+
const query = db.select('*');
|
|
269
|
+
await expect(query.count()).rejects.toThrow('Table name is required. Use .from() method.');
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
246
273
|
describe('Method Availability', () => {
|
|
247
274
|
it('should have run method', () => {
|
|
248
275
|
expect(typeof db.run).toBe('function');
|
|
@@ -260,6 +287,10 @@ describe('BunQL', () => {
|
|
|
260
287
|
expect(typeof db.begin).toBe('function');
|
|
261
288
|
});
|
|
262
289
|
|
|
290
|
+
it('should have transaction method', () => {
|
|
291
|
+
expect(typeof db.transaction).toBe('function');
|
|
292
|
+
});
|
|
293
|
+
|
|
263
294
|
it('should have commit method', () => {
|
|
264
295
|
expect(typeof db.commit).toBe('function');
|
|
265
296
|
});
|
package/src/index.ts
CHANGED
|
@@ -42,35 +42,35 @@ async function executeSql(executor: any, query: string, params?: any[]): Promise
|
|
|
42
42
|
|
|
43
43
|
export class BunQL {
|
|
44
44
|
private sql: SQL;
|
|
45
|
-
private
|
|
45
|
+
private _transaction?: any;
|
|
46
46
|
|
|
47
47
|
constructor(connectionString?: string) {
|
|
48
48
|
this.sql = connectionString ? new SQL(connectionString) : sql;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
select<T = any>(columns?: string | string[]): SelectQuery<T> {
|
|
52
|
-
return new SelectQuery<T>(this.sql, this.
|
|
52
|
+
return new SelectQuery<T>(this.sql, this._transaction, columns);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
update(table: string): UpdateQuery {
|
|
56
|
-
return new UpdateQuery(this.sql, table, this.
|
|
56
|
+
return new UpdateQuery(this.sql, table, this._transaction);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
insert(table: string): InsertQuery {
|
|
60
|
-
return new InsertQuery(this.sql, table, this.
|
|
60
|
+
return new InsertQuery(this.sql, table, this._transaction);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
delete(table: string): DeleteQuery {
|
|
64
|
-
return new DeleteQuery(this.sql, table, this.
|
|
64
|
+
return new DeleteQuery(this.sql, table, this._transaction);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
async run(query: string, params?: any[]): Promise<any> {
|
|
68
|
-
const executor = this.
|
|
68
|
+
const executor = this._transaction || this.sql;
|
|
69
69
|
return await executeSql(executor, query, params);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
async all<T = any>(query: string, params?: any[]): Promise<T[]> {
|
|
73
|
-
const executor = this.
|
|
73
|
+
const executor = this._transaction || this.sql;
|
|
74
74
|
const result = await executeSql(executor, query, params);
|
|
75
75
|
return Array.isArray(result) ? result : [];
|
|
76
76
|
}
|
|
@@ -84,7 +84,16 @@ export class BunQL {
|
|
|
84
84
|
return await this.sql.begin(async (tx) => {
|
|
85
85
|
const transactionBuilder = new BunQL();
|
|
86
86
|
transactionBuilder.sql = this.sql;
|
|
87
|
-
transactionBuilder.
|
|
87
|
+
transactionBuilder._transaction = tx;
|
|
88
|
+
return await callback(transactionBuilder);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async transaction<T>(callback: (trx: BunQL) => Promise<T>): Promise<T> {
|
|
93
|
+
return await this.sql.begin(async (tx) => {
|
|
94
|
+
const transactionBuilder = new BunQL();
|
|
95
|
+
transactionBuilder.sql = this.sql;
|
|
96
|
+
transactionBuilder._transaction = tx;
|
|
88
97
|
return await callback(transactionBuilder);
|
|
89
98
|
});
|
|
90
99
|
}
|
|
@@ -198,6 +207,26 @@ export class SelectQuery<T = any> {
|
|
|
198
207
|
return this.execute();
|
|
199
208
|
}
|
|
200
209
|
|
|
210
|
+
async count(): Promise<number> {
|
|
211
|
+
if (!this.table) {
|
|
212
|
+
throw new Error('Table name is required. Use .from() method.');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let query = `SELECT COUNT(*) as count FROM "${this.table}"`;
|
|
216
|
+
|
|
217
|
+
if (this.whereClauses.length > 0) {
|
|
218
|
+
query += ` WHERE ${this.whereClauses.join(' AND ')}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const executor = this.transaction || this.sql;
|
|
222
|
+
const result = await executeSql(executor, query, this.whereParams);
|
|
223
|
+
// Handle different result formats
|
|
224
|
+
if (Array.isArray(result)) {
|
|
225
|
+
return result[0]?.count || 0;
|
|
226
|
+
}
|
|
227
|
+
return result.count || 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
201
230
|
// Make the query awaitable by implementing then method
|
|
202
231
|
then<TResult1 = T[], TResult2 = never>(
|
|
203
232
|
onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
|
@@ -252,6 +281,22 @@ export class UpdateQuery {
|
|
|
252
281
|
return await executeSql(executor, query, allParams);
|
|
253
282
|
}
|
|
254
283
|
|
|
284
|
+
async count(): Promise<number> {
|
|
285
|
+
let query = `SELECT COUNT(*) as count FROM "${this.table}"`;
|
|
286
|
+
|
|
287
|
+
if (this.whereClauses.length > 0) {
|
|
288
|
+
query += ` WHERE ${this.whereClauses.join(' AND ')}`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const executor = this.transaction || this.sql;
|
|
292
|
+
const result = await executeSql(executor, query, this.whereParams);
|
|
293
|
+
// Handle different result formats
|
|
294
|
+
if (Array.isArray(result)) {
|
|
295
|
+
return result[0]?.count || 0;
|
|
296
|
+
}
|
|
297
|
+
return result.count || 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
255
300
|
// Make the query awaitable by implementing then method
|
|
256
301
|
then<TResult1 = any, TResult2 = never>(
|
|
257
302
|
onfulfilled?: ((value: any) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
|
@@ -315,6 +360,17 @@ export class InsertQuery {
|
|
|
315
360
|
return await executeSql(executor, query, this.valuesArray);
|
|
316
361
|
}
|
|
317
362
|
|
|
363
|
+
async count(): Promise<number> {
|
|
364
|
+
let query = `SELECT COUNT(*) as count FROM "${this.table}"`;
|
|
365
|
+
const executor = this.transaction || this.sql;
|
|
366
|
+
const result = await executeSql(executor, query, []);
|
|
367
|
+
// Handle different result formats
|
|
368
|
+
if (Array.isArray(result)) {
|
|
369
|
+
return result[0]?.count || 0;
|
|
370
|
+
}
|
|
371
|
+
return result.count || 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
318
374
|
// Make the query awaitable by implementing then method
|
|
319
375
|
then<TResult1 = any, TResult2 = never>(
|
|
320
376
|
onfulfilled?: ((value: any) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|
|
@@ -354,6 +410,22 @@ export class DeleteQuery {
|
|
|
354
410
|
return await executeSql(executor, query, this.whereParams);
|
|
355
411
|
}
|
|
356
412
|
|
|
413
|
+
async count(): Promise<number> {
|
|
414
|
+
let query = `SELECT COUNT(*) as count FROM "${this.table}"`;
|
|
415
|
+
|
|
416
|
+
if (this.whereClauses.length > 0) {
|
|
417
|
+
query += ` WHERE ${this.whereClauses.join(' AND ')}`;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const executor = this.transaction || this.sql;
|
|
421
|
+
const result = await executeSql(executor, query, this.whereParams);
|
|
422
|
+
// Handle different result formats
|
|
423
|
+
if (Array.isArray(result)) {
|
|
424
|
+
return result[0]?.count || 0;
|
|
425
|
+
}
|
|
426
|
+
return result.count || 0;
|
|
427
|
+
}
|
|
428
|
+
|
|
357
429
|
// Make the query awaitable by implementing then method
|
|
358
430
|
then<TResult1 = any, TResult2 = never>(
|
|
359
431
|
onfulfilled?: ((value: any) => TResult1 | PromiseLike<TResult1>) | undefined | null,
|