@kysera/dialects 0.7.3 → 0.8.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/README.md +115 -93
- package/dist/index.d.ts +85 -26
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/src/adapters/mssql.ts +207 -0
- package/src/adapters/mysql.ts +73 -51
- package/src/adapters/postgres.ts +67 -42
- package/src/adapters/sqlite.ts +53 -34
- package/src/connection.ts +26 -16
- package/src/factory.ts +20 -16
- package/src/helpers.ts +78 -25
- package/src/index.ts +13 -10
- package/src/types.ts +31 -28
package/README.md
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
- ✅ **Zero Runtime Dependencies** - Only peer dependency on Kysely
|
|
12
12
|
- ✅ **Unified Adapter Interface** - Consistent API across all dialects
|
|
13
|
-
- ✅ **Multi-Database Support** - PostgreSQL, MySQL, and SQLite
|
|
13
|
+
- ✅ **Multi-Database Support** - PostgreSQL, MySQL, and SQLite with dialect-specific adapters
|
|
14
14
|
- ✅ **Error Detection** - Detect unique, foreign key, and not-null constraint violations
|
|
15
15
|
- ✅ **Connection Utilities** - Parse and build connection URLs
|
|
16
16
|
- ✅ **Schema Introspection** - Table existence checks, column enumeration, database size
|
|
17
|
-
- ✅ **Testing Helpers** - Truncate tables
|
|
17
|
+
- ✅ **Testing Helpers** - Truncate tables with recoverable vs critical error handling
|
|
18
18
|
- ✅ **100% Type Safe** - Full TypeScript support with strict mode
|
|
19
19
|
- ✅ **Cross-Runtime** - Works on Node.js, Bun, and Deno
|
|
20
20
|
|
|
@@ -53,7 +53,11 @@ import { getAdapter } from '@kysera/dialects'
|
|
|
53
53
|
|
|
54
54
|
// Create database connection
|
|
55
55
|
const db = new Kysely({
|
|
56
|
-
dialect: new PostgresDialect({
|
|
56
|
+
dialect: new PostgresDialect({
|
|
57
|
+
pool: new Pool({
|
|
58
|
+
/* config */
|
|
59
|
+
})
|
|
60
|
+
})
|
|
57
61
|
})
|
|
58
62
|
|
|
59
63
|
// Get dialect adapter
|
|
@@ -90,8 +94,8 @@ const exists = await tableExists(db, 'users', 'postgres')
|
|
|
90
94
|
const columns = await getTableColumns(db, 'users', 'postgres')
|
|
91
95
|
|
|
92
96
|
// Escape identifiers
|
|
93
|
-
const escaped = escapeIdentifier('user-data', 'mysql')
|
|
94
|
-
const pgEscaped = escapeIdentifier('user-data', 'postgres')
|
|
97
|
+
const escaped = escapeIdentifier('user-data', 'mysql') // `user-data`
|
|
98
|
+
const pgEscaped = escapeIdentifier('user-data', 'postgres') // "user-data"
|
|
95
99
|
|
|
96
100
|
// Error detection
|
|
97
101
|
try {
|
|
@@ -129,9 +133,9 @@ const url = buildConnectionUrl('postgres', {
|
|
|
129
133
|
// 'postgresql://admin:secret@localhost:5432/mydb'
|
|
130
134
|
|
|
131
135
|
// Get default ports
|
|
132
|
-
getDefaultPort('postgres')
|
|
133
|
-
getDefaultPort('mysql')
|
|
134
|
-
getDefaultPort('sqlite')
|
|
136
|
+
getDefaultPort('postgres') // 5432
|
|
137
|
+
getDefaultPort('mysql') // 3306
|
|
138
|
+
getDefaultPort('sqlite') // null
|
|
135
139
|
```
|
|
136
140
|
|
|
137
141
|
---
|
|
@@ -188,18 +192,19 @@ const adapter = postgresAdapter
|
|
|
188
192
|
const adapter = new PostgresAdapter()
|
|
189
193
|
|
|
190
194
|
// Adapter methods
|
|
191
|
-
adapter.getDefaultPort()
|
|
192
|
-
adapter.getCurrentTimestamp()
|
|
193
|
-
adapter.escapeIdentifier('col')
|
|
194
|
-
adapter.formatDate(new Date())
|
|
195
|
+
adapter.getDefaultPort() // 5432
|
|
196
|
+
adapter.getCurrentTimestamp() // 'CURRENT_TIMESTAMP'
|
|
197
|
+
adapter.escapeIdentifier('col') // '"col"'
|
|
198
|
+
adapter.formatDate(new Date()) // ISO 8601 string
|
|
195
199
|
|
|
196
200
|
// PostgreSQL error detection
|
|
197
|
-
adapter.isUniqueConstraintError(error)
|
|
198
|
-
adapter.isForeignKeyError(error)
|
|
199
|
-
adapter.isNotNullError(error)
|
|
201
|
+
adapter.isUniqueConstraintError(error) // Code: 23505
|
|
202
|
+
adapter.isForeignKeyError(error) // Code: 23503
|
|
203
|
+
adapter.isNotNullError(error) // Code: 23502
|
|
200
204
|
```
|
|
201
205
|
|
|
202
206
|
**PostgreSQL-specific features:**
|
|
207
|
+
|
|
203
208
|
- Uses `information_schema.tables` for schema introspection
|
|
204
209
|
- Filters by `table_schema = 'public'` by default
|
|
205
210
|
- Supports `pg_database_size()` for database size queries
|
|
@@ -212,21 +217,22 @@ import { MySQLAdapter, mysqlAdapter } from '@kysera/dialects'
|
|
|
212
217
|
|
|
213
218
|
const adapter = mysqlAdapter
|
|
214
219
|
|
|
215
|
-
adapter.getDefaultPort()
|
|
216
|
-
adapter.getCurrentTimestamp()
|
|
217
|
-
adapter.escapeIdentifier('col')
|
|
218
|
-
adapter.formatDate(new Date())
|
|
220
|
+
adapter.getDefaultPort() // 3306
|
|
221
|
+
adapter.getCurrentTimestamp() // 'CURRENT_TIMESTAMP'
|
|
222
|
+
adapter.escapeIdentifier('col') // '`col`'
|
|
223
|
+
adapter.formatDate(new Date()) // ISO 8601 string
|
|
219
224
|
|
|
220
225
|
// MySQL error detection
|
|
221
|
-
adapter.isUniqueConstraintError(error)
|
|
222
|
-
adapter.isForeignKeyError(error)
|
|
223
|
-
adapter.isNotNullError(error)
|
|
226
|
+
adapter.isUniqueConstraintError(error) // ER_DUP_ENTRY, ER_DUP_KEY
|
|
227
|
+
adapter.isForeignKeyError(error) // ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED
|
|
228
|
+
adapter.isNotNullError(error) // ER_BAD_NULL_ERROR, ER_NO_DEFAULT_FOR_FIELD
|
|
224
229
|
```
|
|
225
230
|
|
|
226
231
|
**MySQL-specific features:**
|
|
232
|
+
|
|
227
233
|
- Uses `information_schema.tables` with `table_schema = database()`
|
|
228
234
|
- Supports backtick identifier escaping
|
|
229
|
-
- Error detection via MySQL error codes (
|
|
235
|
+
- Error detection via MySQL error codes (ER\_\* constants)
|
|
230
236
|
- Database size queries via `information_schema.tables`
|
|
231
237
|
|
|
232
238
|
### SQLite Adapter
|
|
@@ -236,18 +242,19 @@ import { SQLiteAdapter, sqliteAdapter } from '@kysera/dialects'
|
|
|
236
242
|
|
|
237
243
|
const adapter = sqliteAdapter
|
|
238
244
|
|
|
239
|
-
adapter.getDefaultPort()
|
|
240
|
-
adapter.getCurrentTimestamp()
|
|
241
|
-
adapter.escapeIdentifier('col')
|
|
242
|
-
adapter.formatDate(new Date())
|
|
245
|
+
adapter.getDefaultPort() // null (file-based)
|
|
246
|
+
adapter.getCurrentTimestamp() // "datetime('now')"
|
|
247
|
+
adapter.escapeIdentifier('col') // '"col"'
|
|
248
|
+
adapter.formatDate(new Date()) // ISO 8601 string
|
|
243
249
|
|
|
244
250
|
// SQLite error detection (message-based)
|
|
245
|
-
adapter.isUniqueConstraintError(error)
|
|
246
|
-
adapter.isForeignKeyError(error)
|
|
247
|
-
adapter.isNotNullError(error)
|
|
251
|
+
adapter.isUniqueConstraintError(error) // "UNIQUE constraint failed"
|
|
252
|
+
adapter.isForeignKeyError(error) // "FOREIGN KEY constraint failed"
|
|
253
|
+
adapter.isNotNullError(error) // "NOT NULL constraint failed"
|
|
248
254
|
```
|
|
249
255
|
|
|
250
256
|
**SQLite-specific features:**
|
|
257
|
+
|
|
251
258
|
- Uses `sqlite_master` for schema introspection
|
|
252
259
|
- No default port (file-based database)
|
|
253
260
|
- Error detection via message parsing
|
|
@@ -261,8 +268,12 @@ import { registerAdapter, type DialectAdapter } from '@kysera/dialects'
|
|
|
261
268
|
class CustomDialectAdapter implements DialectAdapter {
|
|
262
269
|
readonly dialect = 'custom' as any
|
|
263
270
|
|
|
264
|
-
getDefaultPort() {
|
|
265
|
-
|
|
271
|
+
getDefaultPort() {
|
|
272
|
+
return 9999
|
|
273
|
+
}
|
|
274
|
+
getCurrentTimestamp() {
|
|
275
|
+
return 'NOW()'
|
|
276
|
+
}
|
|
266
277
|
// ... implement all required methods
|
|
267
278
|
}
|
|
268
279
|
|
|
@@ -319,6 +330,7 @@ const sqliteConfig = parseConnectionUrl('sqlite:///path/to/database.db')
|
|
|
319
330
|
```
|
|
320
331
|
|
|
321
332
|
**Supported URL formats:**
|
|
333
|
+
|
|
322
334
|
- `postgresql://[user[:password]@][host][:port]/database[?ssl=true]`
|
|
323
335
|
- `mysql://[user[:password]@][host][:port]/database[?ssl=true]`
|
|
324
336
|
- `sqlite:///path/to/file.db`
|
|
@@ -358,6 +370,7 @@ const sslUrl = buildConnectionUrl('postgres', {
|
|
|
358
370
|
```
|
|
359
371
|
|
|
360
372
|
**Default ports:**
|
|
373
|
+
|
|
361
374
|
- PostgreSQL: 5432
|
|
362
375
|
- MySQL: 3306
|
|
363
376
|
- SQLite: null (file-based)
|
|
@@ -367,9 +380,9 @@ const sslUrl = buildConnectionUrl('postgres', {
|
|
|
367
380
|
```typescript
|
|
368
381
|
import { getDefaultPort } from '@kysera/dialects'
|
|
369
382
|
|
|
370
|
-
getDefaultPort('postgres')
|
|
371
|
-
getDefaultPort('mysql')
|
|
372
|
-
getDefaultPort('sqlite')
|
|
383
|
+
getDefaultPort('postgres') // 5432
|
|
384
|
+
getDefaultPort('mysql') // 3306
|
|
385
|
+
getDefaultPort('sqlite') // null
|
|
373
386
|
```
|
|
374
387
|
|
|
375
388
|
---
|
|
@@ -384,10 +397,7 @@ Detect database constraint violations across different dialects with a unified A
|
|
|
384
397
|
import { isUniqueConstraintError } from '@kysera/dialects'
|
|
385
398
|
|
|
386
399
|
try {
|
|
387
|
-
await db
|
|
388
|
-
.insertInto('users')
|
|
389
|
-
.values({ email: 'existing@example.com', name: 'John' })
|
|
390
|
-
.execute()
|
|
400
|
+
await db.insertInto('users').values({ email: 'existing@example.com', name: 'John' }).execute()
|
|
391
401
|
} catch (error) {
|
|
392
402
|
if (isUniqueConstraintError(error, 'postgres')) {
|
|
393
403
|
console.error('Email already exists')
|
|
@@ -398,11 +408,11 @@ try {
|
|
|
398
408
|
|
|
399
409
|
**Detection criteria:**
|
|
400
410
|
|
|
401
|
-
| Dialect
|
|
402
|
-
|
|
411
|
+
| Dialect | Detection Method |
|
|
412
|
+
| ---------- | ---------------------------------------------------------- |
|
|
403
413
|
| PostgreSQL | Error code `23505` or message contains "unique constraint" |
|
|
404
|
-
| MySQL
|
|
405
|
-
| SQLite
|
|
414
|
+
| MySQL | Error code `ER_DUP_ENTRY` or `ER_DUP_KEY` |
|
|
415
|
+
| SQLite | Message contains "UNIQUE constraint failed" |
|
|
406
416
|
|
|
407
417
|
### Foreign Key Errors
|
|
408
418
|
|
|
@@ -410,10 +420,7 @@ try {
|
|
|
410
420
|
import { isForeignKeyError } from '@kysera/dialects'
|
|
411
421
|
|
|
412
422
|
try {
|
|
413
|
-
await db
|
|
414
|
-
.insertInto('posts')
|
|
415
|
-
.values({ user_id: 999, title: 'Post', content: '...' })
|
|
416
|
-
.execute()
|
|
423
|
+
await db.insertInto('posts').values({ user_id: 999, title: 'Post', content: '...' }).execute()
|
|
417
424
|
} catch (error) {
|
|
418
425
|
if (isForeignKeyError(error, 'postgres')) {
|
|
419
426
|
console.error('User does not exist')
|
|
@@ -424,11 +431,11 @@ try {
|
|
|
424
431
|
|
|
425
432
|
**Detection criteria:**
|
|
426
433
|
|
|
427
|
-
| Dialect
|
|
428
|
-
|
|
434
|
+
| Dialect | Detection Method |
|
|
435
|
+
| ---------- | --------------------------------------------------------------- |
|
|
429
436
|
| PostgreSQL | Error code `23503` or message contains "foreign key constraint" |
|
|
430
|
-
| MySQL
|
|
431
|
-
| SQLite
|
|
437
|
+
| MySQL | Error code `ER_NO_REFERENCED_ROW` or `ER_ROW_IS_REFERENCED` |
|
|
438
|
+
| SQLite | Message contains "FOREIGN KEY constraint failed" |
|
|
432
439
|
|
|
433
440
|
### Not-Null Errors
|
|
434
441
|
|
|
@@ -450,11 +457,11 @@ try {
|
|
|
450
457
|
|
|
451
458
|
**Detection criteria:**
|
|
452
459
|
|
|
453
|
-
| Dialect
|
|
454
|
-
|
|
460
|
+
| Dialect | Detection Method |
|
|
461
|
+
| ---------- | ------------------------------------------------------------ |
|
|
455
462
|
| PostgreSQL | Error code `23502` or message contains "not-null constraint" |
|
|
456
|
-
| MySQL
|
|
457
|
-
| SQLite
|
|
463
|
+
| MySQL | Error code `ER_BAD_NULL_ERROR` or `ER_NO_DEFAULT_FOR_FIELD` |
|
|
464
|
+
| SQLite | Message contains "NOT NULL constraint failed" |
|
|
458
465
|
|
|
459
466
|
### Adapter-based Error Detection
|
|
460
467
|
|
|
@@ -525,19 +532,19 @@ console.log(`Database has ${tables.length} tables`)
|
|
|
525
532
|
import { escapeIdentifier } from '@kysera/dialects'
|
|
526
533
|
|
|
527
534
|
// PostgreSQL (double quotes)
|
|
528
|
-
escapeIdentifier('user-data', 'postgres')
|
|
529
|
-
escapeIdentifier('select', 'postgres')
|
|
535
|
+
escapeIdentifier('user-data', 'postgres') // "user-data"
|
|
536
|
+
escapeIdentifier('select', 'postgres') // "select"
|
|
530
537
|
|
|
531
538
|
// MySQL (backticks)
|
|
532
|
-
escapeIdentifier('user-data', 'mysql')
|
|
533
|
-
escapeIdentifier('order', 'mysql')
|
|
539
|
+
escapeIdentifier('user-data', 'mysql') // `user-data`
|
|
540
|
+
escapeIdentifier('order', 'mysql') // `order`
|
|
534
541
|
|
|
535
542
|
// SQLite (double quotes)
|
|
536
|
-
escapeIdentifier('user-data', 'sqlite')
|
|
543
|
+
escapeIdentifier('user-data', 'sqlite') // "user-data"
|
|
537
544
|
|
|
538
545
|
// Handles quotes in identifiers
|
|
539
|
-
escapeIdentifier('user"data', 'postgres')
|
|
540
|
-
escapeIdentifier('user`data', 'mysql')
|
|
546
|
+
escapeIdentifier('user"data', 'postgres') // "user""data"
|
|
547
|
+
escapeIdentifier('user`data', 'mysql') // `user``data`
|
|
541
548
|
```
|
|
542
549
|
|
|
543
550
|
### Timestamp Utilities
|
|
@@ -548,13 +555,13 @@ escapeIdentifier('user`data', 'mysql') // `user``data`
|
|
|
548
555
|
import { getCurrentTimestamp } from '@kysera/dialects'
|
|
549
556
|
|
|
550
557
|
// PostgreSQL
|
|
551
|
-
getCurrentTimestamp('postgres')
|
|
558
|
+
getCurrentTimestamp('postgres') // 'CURRENT_TIMESTAMP'
|
|
552
559
|
|
|
553
560
|
// MySQL
|
|
554
|
-
getCurrentTimestamp('mysql')
|
|
561
|
+
getCurrentTimestamp('mysql') // 'CURRENT_TIMESTAMP'
|
|
555
562
|
|
|
556
563
|
// SQLite
|
|
557
|
-
getCurrentTimestamp('sqlite')
|
|
564
|
+
getCurrentTimestamp('sqlite') // "datetime('now')"
|
|
558
565
|
|
|
559
566
|
// Usage in queries
|
|
560
567
|
const timestamp = getCurrentTimestamp('postgres')
|
|
@@ -572,9 +579,9 @@ import { formatDate } from '@kysera/dialects'
|
|
|
572
579
|
const date = new Date('2024-01-15T10:30:00Z')
|
|
573
580
|
|
|
574
581
|
// All dialects return ISO 8601 format
|
|
575
|
-
formatDate(date, 'postgres')
|
|
576
|
-
formatDate(date, 'mysql')
|
|
577
|
-
formatDate(date, 'sqlite')
|
|
582
|
+
formatDate(date, 'postgres') // '2024-01-15T10:30:00.000Z'
|
|
583
|
+
formatDate(date, 'mysql') // '2024-01-15T10:30:00.000Z'
|
|
584
|
+
formatDate(date, 'sqlite') // '2024-01-15T10:30:00.000Z'
|
|
578
585
|
```
|
|
579
586
|
|
|
580
587
|
### Database Management
|
|
@@ -611,7 +618,13 @@ await truncateAllTables(db, 'postgres', ['migrations', 'schema_version'])
|
|
|
611
618
|
|
|
612
619
|
**Warning:** This permanently deletes all data. Use only in test environments.
|
|
613
620
|
|
|
621
|
+
**Error Handling:** The function uses sophisticated error classification:
|
|
622
|
+
- **Recoverable errors:** Table not found, already truncated → Logged as warnings, execution continues
|
|
623
|
+
- **Critical errors:** Permission denied, foreign key violations → Thrown immediately
|
|
624
|
+
- **Unknown errors:** All other errors → Thrown with full error context
|
|
625
|
+
|
|
614
626
|
**Behavior:**
|
|
627
|
+
|
|
615
628
|
- PostgreSQL: `TRUNCATE TABLE ... CASCADE`
|
|
616
629
|
- MySQL: `TRUNCATE TABLE ...`
|
|
617
630
|
- SQLite: `DELETE FROM ...` (no TRUNCATE support)
|
|
@@ -622,11 +635,12 @@ await truncateAllTables(db, 'postgres', ['migrations', 'schema_version'])
|
|
|
622
635
|
|
|
623
636
|
### Factory Functions
|
|
624
637
|
|
|
625
|
-
#### `getAdapter(dialect:
|
|
638
|
+
#### `getAdapter(dialect: Dialect): DialectAdapter`
|
|
626
639
|
|
|
627
640
|
Get singleton adapter for specified dialect.
|
|
628
641
|
|
|
629
642
|
**Parameters:**
|
|
643
|
+
|
|
630
644
|
- `dialect` - `'postgres' | 'mysql' | 'sqlite'`
|
|
631
645
|
|
|
632
646
|
**Returns:** Dialect adapter instance
|
|
@@ -635,11 +649,12 @@ Get singleton adapter for specified dialect.
|
|
|
635
649
|
|
|
636
650
|
---
|
|
637
651
|
|
|
638
|
-
#### `createDialectAdapter(dialect:
|
|
652
|
+
#### `createDialectAdapter(dialect: Dialect): DialectAdapter`
|
|
639
653
|
|
|
640
654
|
Create new adapter instance.
|
|
641
655
|
|
|
642
656
|
**Parameters:**
|
|
657
|
+
|
|
643
658
|
- `dialect` - `'postgres' | 'mysql' | 'sqlite'`
|
|
644
659
|
|
|
645
660
|
**Returns:** New dialect adapter instance
|
|
@@ -653,6 +668,7 @@ Create new adapter instance.
|
|
|
653
668
|
Register custom dialect adapter.
|
|
654
669
|
|
|
655
670
|
**Parameters:**
|
|
671
|
+
|
|
656
672
|
- `adapter` - Custom adapter implementing `DialectAdapter` interface
|
|
657
673
|
|
|
658
674
|
**Use Case:** Extend with custom database support.
|
|
@@ -666,9 +682,11 @@ Register custom dialect adapter.
|
|
|
666
682
|
Interface for dialect-specific operations.
|
|
667
683
|
|
|
668
684
|
**Properties:**
|
|
669
|
-
|
|
685
|
+
|
|
686
|
+
- `dialect: Dialect` - The dialect this adapter handles
|
|
670
687
|
|
|
671
688
|
**Methods:**
|
|
689
|
+
|
|
672
690
|
- `getDefaultPort(): number | null` - Get default port for this dialect
|
|
673
691
|
- `getCurrentTimestamp(): string` - Get SQL expression for current timestamp
|
|
674
692
|
- `escapeIdentifier(identifier: string): string` - Escape identifier for this dialect
|
|
@@ -692,17 +710,19 @@ Interface for dialect-specific operations.
|
|
|
692
710
|
Parse connection URL into configuration object.
|
|
693
711
|
|
|
694
712
|
**Parameters:**
|
|
713
|
+
|
|
695
714
|
- `url` - Database connection URL
|
|
696
715
|
|
|
697
716
|
**Returns:** `ConnectionConfig` object
|
|
698
717
|
|
|
699
718
|
---
|
|
700
719
|
|
|
701
|
-
#### `buildConnectionUrl(dialect:
|
|
720
|
+
#### `buildConnectionUrl(dialect: Dialect, config: ConnectionConfig): string`
|
|
702
721
|
|
|
703
722
|
Build connection URL from configuration.
|
|
704
723
|
|
|
705
724
|
**Parameters:**
|
|
725
|
+
|
|
706
726
|
- `dialect` - Database dialect
|
|
707
727
|
- `config` - Connection configuration
|
|
708
728
|
|
|
@@ -710,11 +730,12 @@ Build connection URL from configuration.
|
|
|
710
730
|
|
|
711
731
|
---
|
|
712
732
|
|
|
713
|
-
#### `getDefaultPort(dialect:
|
|
733
|
+
#### `getDefaultPort(dialect: Dialect): number | null`
|
|
714
734
|
|
|
715
735
|
Get default port for dialect.
|
|
716
736
|
|
|
717
737
|
**Parameters:**
|
|
738
|
+
|
|
718
739
|
- `dialect` - Database dialect
|
|
719
740
|
|
|
720
741
|
**Returns:** Port number or null for SQLite
|
|
@@ -723,67 +744,67 @@ Get default port for dialect.
|
|
|
723
744
|
|
|
724
745
|
### Helper Functions
|
|
725
746
|
|
|
726
|
-
#### `tableExists(db: Kysely<any>, tableName: string, dialect:
|
|
747
|
+
#### `tableExists(db: Kysely<any>, tableName: string, dialect: Dialect): Promise<boolean>`
|
|
727
748
|
|
|
728
749
|
Check if table exists.
|
|
729
750
|
|
|
730
751
|
---
|
|
731
752
|
|
|
732
|
-
#### `getTableColumns(db: Kysely<any>, tableName: string, dialect:
|
|
753
|
+
#### `getTableColumns(db: Kysely<any>, tableName: string, dialect: Dialect): Promise<string[]>`
|
|
733
754
|
|
|
734
755
|
Get column names for a table.
|
|
735
756
|
|
|
736
757
|
---
|
|
737
758
|
|
|
738
|
-
#### `getTables(db: Kysely<any>, dialect:
|
|
759
|
+
#### `getTables(db: Kysely<any>, dialect: Dialect): Promise<string[]>`
|
|
739
760
|
|
|
740
761
|
Get all tables in database.
|
|
741
762
|
|
|
742
763
|
---
|
|
743
764
|
|
|
744
|
-
#### `escapeIdentifier(identifier: string, dialect:
|
|
765
|
+
#### `escapeIdentifier(identifier: string, dialect: Dialect): string`
|
|
745
766
|
|
|
746
767
|
Escape identifier for SQL.
|
|
747
768
|
|
|
748
769
|
---
|
|
749
770
|
|
|
750
|
-
#### `getCurrentTimestamp(dialect:
|
|
771
|
+
#### `getCurrentTimestamp(dialect: Dialect): string`
|
|
751
772
|
|
|
752
773
|
Get SQL expression for current timestamp.
|
|
753
774
|
|
|
754
775
|
---
|
|
755
776
|
|
|
756
|
-
#### `formatDate(date: Date, dialect:
|
|
777
|
+
#### `formatDate(date: Date, dialect: Dialect): string`
|
|
757
778
|
|
|
758
779
|
Format date for SQL.
|
|
759
780
|
|
|
760
781
|
---
|
|
761
782
|
|
|
762
|
-
#### `isUniqueConstraintError(error: unknown, dialect:
|
|
783
|
+
#### `isUniqueConstraintError(error: unknown, dialect: Dialect): boolean`
|
|
763
784
|
|
|
764
785
|
Check if error is unique constraint violation.
|
|
765
786
|
|
|
766
787
|
---
|
|
767
788
|
|
|
768
|
-
#### `isForeignKeyError(error: unknown, dialect:
|
|
789
|
+
#### `isForeignKeyError(error: unknown, dialect: Dialect): boolean`
|
|
769
790
|
|
|
770
791
|
Check if error is foreign key violation.
|
|
771
792
|
|
|
772
793
|
---
|
|
773
794
|
|
|
774
|
-
#### `isNotNullError(error: unknown, dialect:
|
|
795
|
+
#### `isNotNullError(error: unknown, dialect: Dialect): boolean`
|
|
775
796
|
|
|
776
797
|
Check if error is not-null violation.
|
|
777
798
|
|
|
778
799
|
---
|
|
779
800
|
|
|
780
|
-
#### `getDatabaseSize(db: Kysely<any>, databaseName: string | undefined, dialect:
|
|
801
|
+
#### `getDatabaseSize(db: Kysely<any>, databaseName: string | undefined, dialect: Dialect): Promise<number>`
|
|
781
802
|
|
|
782
803
|
Get database size in bytes.
|
|
783
804
|
|
|
784
805
|
---
|
|
785
806
|
|
|
786
|
-
#### `truncateAllTables(db: Kysely<any>, dialect:
|
|
807
|
+
#### `truncateAllTables(db: Kysely<any>, dialect: Dialect, exclude?: string[]): Promise<void>`
|
|
787
808
|
|
|
788
809
|
Truncate all tables in database.
|
|
789
810
|
|
|
@@ -791,9 +812,9 @@ Truncate all tables in database.
|
|
|
791
812
|
|
|
792
813
|
### Types
|
|
793
814
|
|
|
794
|
-
#### `type
|
|
815
|
+
#### `type Dialect = 'postgres' | 'mysql' | 'sqlite' | 'mssql'`
|
|
795
816
|
|
|
796
|
-
Supported database dialects.
|
|
817
|
+
Supported database dialects. Re-exported from `@kysera/core`.
|
|
797
818
|
|
|
798
819
|
---
|
|
799
820
|
|
|
@@ -833,16 +854,17 @@ interface DatabaseErrorLike {
|
|
|
833
854
|
|
|
834
855
|
```typescript
|
|
835
856
|
// ✅ Good: Works with any dialect
|
|
836
|
-
import { getAdapter } from '@kysera/dialects'
|
|
857
|
+
import { getAdapter, type Dialect } from '@kysera/dialects'
|
|
837
858
|
|
|
838
|
-
function checkSchema(db: Kysely<any>, dialect:
|
|
859
|
+
function checkSchema(db: Kysely<any>, dialect: Dialect) {
|
|
839
860
|
const adapter = getAdapter(dialect)
|
|
840
861
|
return adapter.tableExists(db, 'users')
|
|
841
862
|
}
|
|
842
863
|
|
|
843
864
|
// ❌ Bad: Hard-coded dialect logic
|
|
844
865
|
function checkSchema(db: Kysely<any>) {
|
|
845
|
-
return db
|
|
866
|
+
return db
|
|
867
|
+
.selectFrom('information_schema.tables')
|
|
846
868
|
.where('table_name', '=', 'users')
|
|
847
869
|
.executeTakeFirst()
|
|
848
870
|
}
|
|
@@ -862,10 +884,10 @@ const exists = await adapter.tableExists(db, 'users')
|
|
|
862
884
|
### 3. Centralize Error Handling
|
|
863
885
|
|
|
864
886
|
```typescript
|
|
865
|
-
import { getAdapter } from '@kysera/dialects'
|
|
887
|
+
import { getAdapter, type Dialect } from '@kysera/dialects'
|
|
866
888
|
import { parseDatabaseError } from '@kysera/core'
|
|
867
889
|
|
|
868
|
-
async function handleDatabaseError(error: unknown, dialect:
|
|
890
|
+
async function handleDatabaseError(error: unknown, dialect: Dialect) {
|
|
869
891
|
const adapter = getAdapter(dialect)
|
|
870
892
|
|
|
871
893
|
if (adapter.isUniqueConstraintError(error)) {
|
|
@@ -929,11 +951,11 @@ try {
|
|
|
929
951
|
### 6. Escape Dynamic Identifiers
|
|
930
952
|
|
|
931
953
|
```typescript
|
|
932
|
-
import { escapeIdentifier } from '@kysera/dialects'
|
|
954
|
+
import { escapeIdentifier, type Dialect } from '@kysera/dialects'
|
|
933
955
|
import { sql } from 'kysely'
|
|
934
956
|
|
|
935
957
|
// ✅ Good: Escape dynamic table/column names
|
|
936
|
-
function selectFromTable(tableName: string, dialect:
|
|
958
|
+
function selectFromTable(tableName: string, dialect: Dialect) {
|
|
937
959
|
const escaped = escapeIdentifier(tableName, dialect)
|
|
938
960
|
return db.selectFrom(sql.raw(escaped)).selectAll()
|
|
939
961
|
}
|