bunql 1.0.1-dev.2 → 1.0.1-dev.4

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.
Files changed (3) hide show
  1. package/README.md +39 -1
  2. package/package.json +1 -1
  3. package/src/schema.ts +435 -45
package/README.md CHANGED
@@ -255,7 +255,45 @@ console.log('Transaction completed, user ID:', result);
255
255
 
256
256
  ## Schema Management
257
257
 
258
- BunQL provides a comprehensive schema management API that can replace Bunely for database schema operations.
258
+ BunQL provides a comprehensive schema management API that can replace Bunely for database schema operations. The schema API is **database-agnostic** and supports SQLite, PostgreSQL, and MySQL.
259
+
260
+ ### Database Support
261
+
262
+ BunQL automatically detects your database type and adapts the schema operations accordingly:
263
+
264
+ - **SQLite**: Uses `PRAGMA` statements and `sqlite_master` table
265
+ - **PostgreSQL**: Uses `information_schema` views and `pg_*` system tables
266
+ - **MySQL**: Uses `information_schema` views and `SHOW` statements
267
+
268
+ ### Database Detection
269
+
270
+ ```typescript
271
+ // Get database information
272
+ const dbInfo = await db.schema.getDatabaseInfo();
273
+ console.log(`Database: ${dbInfo.type} ${dbInfo.version}`);
274
+
275
+ ### Database-Specific Features
276
+
277
+ BunQL handles database differences automatically:
278
+
279
+ **Column Types:**
280
+ - `INTEGER` → `INTEGER` (SQLite), `INTEGER` (PostgreSQL), `INT` (MySQL)
281
+ - `BOOLEAN` → `INTEGER` (SQLite), `BOOLEAN` (PostgreSQL/MySQL)
282
+ - `BLOB` → `BLOB` (SQLite/MySQL), `BYTEA` (PostgreSQL)
283
+
284
+ **Auto-increment:**
285
+ - SQLite: `AUTOINCREMENT`
286
+ - MySQL: `AUTO_INCREMENT`
287
+ - PostgreSQL: Uses `SERIAL` or `IDENTITY` (handled automatically)
288
+
289
+ **Quoting:**
290
+ - SQLite/PostgreSQL: `"table_name"`
291
+ - MySQL: `` `table_name` ``
292
+
293
+ **Schema Introspection:**
294
+ - SQLite: `PRAGMA table_info()`, `sqlite_master`
295
+ - PostgreSQL: `information_schema.columns`, `pg_indexes`
296
+ - MySQL: `information_schema.tables`, `information_schema.statistics`
259
297
 
260
298
  ### Creating Tables
261
299
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunql",
3
- "version": "1.0.1-dev.2",
3
+ "version": "1.0.1-dev.4",
4
4
  "description": "A fluent SQL query builder for Bun with transaction support",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
package/src/schema.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  // Import will be handled by the main index file to avoid circular dependency
2
2
 
3
+ export interface DatabaseInfo {
4
+ type: 'sqlite' | 'postgresql' | 'mysql';
5
+ version?: string;
6
+ }
7
+
3
8
  export interface ColumnDefinition {
4
9
  name: string;
5
10
  type: string;
@@ -49,10 +54,31 @@ export class SchemaBuilder {
49
54
  private indexes: IndexInfo[] = [];
50
55
  private foreignKeys: ForeignKeyInfo[] = [];
51
56
  private options: CreateTableOptions = {};
57
+ private databaseType: 'sqlite' | 'postgresql' | 'mysql' = 'sqlite';
52
58
 
53
59
  constructor(db: any, tableName: string) {
54
60
  this.db = db;
55
61
  this.tableName = tableName;
62
+ this.detectDatabaseType();
63
+ }
64
+
65
+ /**
66
+ * Detect the database type
67
+ */
68
+ private async detectDatabaseType(): Promise<void> {
69
+ try {
70
+ const connectionString = process.env.DATABASE_URL || '';
71
+
72
+ if (connectionString.includes('postgresql://') || connectionString.includes('postgres://')) {
73
+ this.databaseType = 'postgresql';
74
+ } else if (connectionString.includes('mysql://') || connectionString.includes('mariadb://')) {
75
+ this.databaseType = 'mysql';
76
+ } else {
77
+ this.databaseType = 'sqlite';
78
+ }
79
+ } catch (error) {
80
+ this.databaseType = 'sqlite';
81
+ }
56
82
  }
57
83
 
58
84
  /**
@@ -103,6 +129,7 @@ export class SchemaBuilder {
103
129
  throw new Error('At least one column must be defined');
104
130
  }
105
131
 
132
+ await this.detectDatabaseType();
106
133
  const sql = this.buildCreateTableSQL();
107
134
  await this.db.run(sql);
108
135
 
@@ -113,18 +140,33 @@ export class SchemaBuilder {
113
140
  }
114
141
 
115
142
  private buildCreateTableSQL(): string {
143
+
116
144
  const ifNotExists = this.options.ifNotExists ? 'IF NOT EXISTS ' : '';
117
145
  const temporary = this.options.temporary ? 'TEMPORARY ' : '';
118
146
 
119
- let sql = `CREATE ${temporary}TABLE ${ifNotExists}"${this.tableName}" (`;
147
+ let sql = `CREATE ${temporary}TABLE ${ifNotExists}`;
148
+
149
+ // Handle different quoting for different databases
150
+ switch (this.databaseType) {
151
+ case 'postgresql':
152
+ sql += `"${this.tableName}" (`;
153
+ break;
154
+ case 'mysql':
155
+ sql += `\`${this.tableName}\` (`;
156
+ break;
157
+ case 'sqlite':
158
+ default:
159
+ sql += `"${this.tableName}" (`;
160
+ break;
161
+ }
120
162
 
121
163
  const columnDefinitions = this.columns.map(col => {
122
- let def = `"${col.name}" ${col.type}`;
164
+ let def = this.quoteColumn(col.name) + ' ' + this.mapColumnType(col.type);
123
165
 
124
166
  if (col.primaryKey) {
125
167
  def += ' PRIMARY KEY';
126
- if (col.autoIncrement) {
127
- def += ' AUTOINCREMENT';
168
+ if (col.autoIncrement && this.databaseType !== 'postgresql') {
169
+ def += this.getAutoIncrementKeyword();
128
170
  }
129
171
  }
130
172
 
@@ -137,11 +179,7 @@ export class SchemaBuilder {
137
179
  }
138
180
 
139
181
  if (col.defaultValue !== undefined) {
140
- if (typeof col.defaultValue === 'string') {
141
- def += ` DEFAULT '${col.defaultValue}'`;
142
- } else {
143
- def += ` DEFAULT ${col.defaultValue}`;
144
- }
182
+ def += this.formatDefaultValue(col.defaultValue);
145
183
  }
146
184
 
147
185
  return def;
@@ -149,10 +187,10 @@ export class SchemaBuilder {
149
187
 
150
188
  // Add foreign key constraints
151
189
  const foreignKeyDefinitions = this.foreignKeys.map(fk => {
152
- const columns = fk.columns.map(col => `"${col}"`).join(', ');
153
- const referencedColumns = fk.referencedColumns.map(col => `"${col}"`).join(', ');
190
+ const columns = fk.columns.map(col => this.quoteColumn(col)).join(', ');
191
+ const referencedColumns = fk.referencedColumns.map(col => this.quoteColumn(col)).join(', ');
154
192
 
155
- let fkDef = `FOREIGN KEY (${columns}) REFERENCES "${fk.referencedTable}" (${referencedColumns})`;
193
+ let fkDef = `FOREIGN KEY (${columns}) REFERENCES ${this.quoteTable(fk.referencedTable)} (${referencedColumns})`;
156
194
 
157
195
  if (fk.onDelete) {
158
196
  fkDef += ` ON DELETE ${fk.onDelete}`;
@@ -172,6 +210,115 @@ export class SchemaBuilder {
172
210
  return sql;
173
211
  }
174
212
 
213
+ /**
214
+ * Quote column names based on database type
215
+ */
216
+ private quoteColumn(columnName: string): string {
217
+ switch (this.databaseType) {
218
+ case 'postgresql':
219
+ return `"${columnName}"`;
220
+ case 'mysql':
221
+ return `\`${columnName}\``;
222
+ case 'sqlite':
223
+ default:
224
+ return `"${columnName}"`;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Quote table names based on database type
230
+ */
231
+ private quoteTable(tableName: string): string {
232
+ switch (this.databaseType) {
233
+ case 'postgresql':
234
+ return `"${tableName}"`;
235
+ case 'mysql':
236
+ return `\`${tableName}\``;
237
+ case 'sqlite':
238
+ default:
239
+ return `"${tableName}"`;
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Map generic types to database-specific types
245
+ */
246
+ private mapColumnType(type: string): string {
247
+ const typeMap: Record<string, Record<string, string>> = {
248
+ sqlite: {
249
+ 'INTEGER': 'INTEGER',
250
+ 'TEXT': 'TEXT',
251
+ 'REAL': 'REAL',
252
+ 'BLOB': 'BLOB',
253
+ 'BOOLEAN': 'INTEGER',
254
+ 'DATE': 'TEXT',
255
+ 'DATETIME': 'TEXT',
256
+ 'TIMESTAMP': 'TEXT'
257
+ },
258
+ postgresql: {
259
+ 'INTEGER': 'INTEGER',
260
+ 'TEXT': 'TEXT',
261
+ 'REAL': 'REAL',
262
+ 'BLOB': 'BYTEA',
263
+ 'BOOLEAN': 'BOOLEAN',
264
+ 'DATE': 'DATE',
265
+ 'DATETIME': 'TIMESTAMP',
266
+ 'TIMESTAMP': 'TIMESTAMP'
267
+ },
268
+ mysql: {
269
+ 'INTEGER': 'INT',
270
+ 'TEXT': 'TEXT',
271
+ 'REAL': 'DOUBLE',
272
+ 'BLOB': 'BLOB',
273
+ 'BOOLEAN': 'BOOLEAN',
274
+ 'DATE': 'DATE',
275
+ 'DATETIME': 'DATETIME',
276
+ 'TIMESTAMP': 'TIMESTAMP'
277
+ }
278
+ };
279
+
280
+ return typeMap[this.databaseType]?.[type.toUpperCase()] || type;
281
+ }
282
+
283
+ /**
284
+ * Get auto-increment keyword based on database type
285
+ */
286
+ private getAutoIncrementKeyword(): string {
287
+ switch (this.databaseType) {
288
+ case 'mysql':
289
+ return ' AUTO_INCREMENT';
290
+ case 'sqlite':
291
+ return ' AUTOINCREMENT';
292
+ case 'postgresql':
293
+ return ''; // PostgreSQL uses SERIAL or IDENTITY
294
+ default:
295
+ return ' AUTOINCREMENT';
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Format default values based on database type
301
+ */
302
+ private formatDefaultValue(value: any): string {
303
+ if (typeof value === 'string') {
304
+ return ` DEFAULT '${value}'`;
305
+ } else if (value === null) {
306
+ return ' DEFAULT NULL';
307
+ } else if (typeof value === 'boolean') {
308
+ switch (this.databaseType) {
309
+ case 'postgresql':
310
+ return ` DEFAULT ${value}`;
311
+ case 'mysql':
312
+ return ` DEFAULT ${value ? 1 : 0}`;
313
+ case 'sqlite':
314
+ default:
315
+ return ` DEFAULT ${value ? 1 : 0}`;
316
+ }
317
+ } else {
318
+ return ` DEFAULT ${value}`;
319
+ }
320
+ }
321
+
175
322
  private async createIndex(index: IndexInfo): Promise<void> {
176
323
  const unique = index.unique ? 'UNIQUE ' : '';
177
324
  const indexName = index.name || `idx_${this.tableName}_${index.columns.join('_')}`;
@@ -410,9 +557,65 @@ export class AlterTableBuilder {
410
557
 
411
558
  export class Schema {
412
559
  private db: any; // Will be BunQL instance
560
+ private databaseType: 'sqlite' | 'postgresql' | 'mysql' = 'sqlite';
413
561
 
414
562
  constructor(db: any) {
415
563
  this.db = db;
564
+ this.detectDatabaseType();
565
+ }
566
+
567
+ /**
568
+ * Detect the database type from connection string or environment
569
+ */
570
+ private async detectDatabaseType(): Promise<void> {
571
+ try {
572
+ // Try to detect from connection string or environment
573
+ const connectionString = process.env.DATABASE_URL || '';
574
+
575
+ if (connectionString.includes('postgresql://') || connectionString.includes('postgres://')) {
576
+ this.databaseType = 'postgresql';
577
+ } else if (connectionString.includes('mysql://') || connectionString.includes('mariadb://')) {
578
+ this.databaseType = 'mysql';
579
+ } else {
580
+ // Default to SQLite for local development
581
+ this.databaseType = 'sqlite';
582
+ }
583
+ } catch (error) {
584
+ // Fallback to SQLite
585
+ this.databaseType = 'sqlite';
586
+ }
587
+ }
588
+
589
+ /**
590
+ * Get database information
591
+ */
592
+ async getDatabaseInfo(): Promise<DatabaseInfo> {
593
+ await this.detectDatabaseType();
594
+
595
+ let version = '';
596
+ try {
597
+ switch (this.databaseType) {
598
+ case 'postgresql':
599
+ const pgResult = await this.db.get('SELECT version() as version');
600
+ version = pgResult?.version || '';
601
+ break;
602
+ case 'mysql':
603
+ const mysqlResult = await this.db.get('SELECT VERSION() as version');
604
+ version = mysqlResult?.version || '';
605
+ break;
606
+ case 'sqlite':
607
+ const sqliteResult = await this.db.get('SELECT sqlite_version() as version');
608
+ version = sqliteResult?.version || '';
609
+ break;
610
+ }
611
+ } catch (error) {
612
+ // Ignore version detection errors
613
+ }
614
+
615
+ return {
616
+ type: this.databaseType,
617
+ version
618
+ };
416
619
  }
417
620
 
418
621
  /**
@@ -442,10 +645,36 @@ export class Schema {
442
645
  * Check if a table exists
443
646
  */
444
647
  async hasTable(tableName: string): Promise<boolean> {
445
- const result = await this.db.get(`
446
- SELECT name FROM sqlite_master
447
- WHERE type='table' AND name = ?
448
- `, [tableName]);
648
+ await this.detectDatabaseType();
649
+
650
+ let query: string;
651
+ let params: any[] = [tableName];
652
+
653
+ switch (this.databaseType) {
654
+ case 'postgresql':
655
+ query = `
656
+ SELECT table_name
657
+ FROM information_schema.tables
658
+ WHERE table_schema = 'public' AND table_name = ?
659
+ `;
660
+ break;
661
+ case 'mysql':
662
+ query = `
663
+ SELECT table_name
664
+ FROM information_schema.tables
665
+ WHERE table_schema = DATABASE() AND table_name = ?
666
+ `;
667
+ break;
668
+ case 'sqlite':
669
+ default:
670
+ query = `
671
+ SELECT name FROM sqlite_master
672
+ WHERE type='table' AND name = ?
673
+ `;
674
+ break;
675
+ }
676
+
677
+ const result = await this.db.get(query, params);
449
678
  return result !== null;
450
679
  }
451
680
 
@@ -453,16 +682,62 @@ export class Schema {
453
682
  * Get information about a table
454
683
  */
455
684
  async getTableInfo(tableName: string): Promise<ColumnDefinition[]> {
456
- const result = await this.db.all(`
457
- PRAGMA table_info("${tableName}")
458
- `);
685
+ await this.detectDatabaseType();
686
+
687
+ let query: string;
688
+ let params: any[] = [tableName];
689
+
690
+ switch (this.databaseType) {
691
+ case 'postgresql':
692
+ query = `
693
+ SELECT
694
+ column_name as name,
695
+ data_type as type,
696
+ is_nullable = 'NO' as notnull,
697
+ column_default as dflt_value,
698
+ CASE WHEN pk.column_name IS NOT NULL THEN 1 ELSE 0 END as pk
699
+ FROM information_schema.columns c
700
+ LEFT JOIN (
701
+ SELECT ku.column_name
702
+ FROM information_schema.table_constraints tc
703
+ JOIN information_schema.key_column_usage ku
704
+ ON tc.constraint_name = ku.constraint_name
705
+ WHERE tc.constraint_type = 'PRIMARY KEY'
706
+ AND tc.table_name = ?
707
+ ) pk ON c.column_name = pk.column_name
708
+ WHERE c.table_name = ?
709
+ ORDER BY c.ordinal_position
710
+ `;
711
+ params = [tableName, tableName];
712
+ break;
713
+ case 'mysql':
714
+ query = `
715
+ SELECT
716
+ COLUMN_NAME as name,
717
+ DATA_TYPE as type,
718
+ IS_NULLABLE = 'NO' as notnull,
719
+ COLUMN_DEFAULT as dflt_value,
720
+ CASE WHEN COLUMN_KEY = 'PRI' THEN 1 ELSE 0 END as pk
721
+ FROM information_schema.columns
722
+ WHERE table_schema = DATABASE() AND table_name = ?
723
+ ORDER BY ordinal_position
724
+ `;
725
+ break;
726
+ case 'sqlite':
727
+ default:
728
+ query = `PRAGMA table_info("${tableName}")`;
729
+ params = [];
730
+ break;
731
+ }
732
+
733
+ const result = await this.db.all(query, params);
459
734
 
460
735
  return result.map((row: any) => ({
461
736
  name: row.name,
462
737
  type: row.type,
463
- notNull: row.notnull === 1,
464
- primaryKey: row.pk === 1,
465
- unique: false, // SQLite doesn't expose this in table_info
738
+ notNull: row.notnull === 1 || row.notnull === true,
739
+ primaryKey: row.pk === 1 || row.pk === true,
740
+ unique: false, // Would need additional queries to detect this
466
741
  defaultValue: row.dflt_value,
467
742
  autoIncrement: false // Would need additional logic to detect this
468
743
  }));
@@ -472,34 +747,103 @@ export class Schema {
472
747
  * Get all tables in the database
473
748
  */
474
749
  async getTables(): Promise<string[]> {
475
- const result = await this.db.all(`
476
- SELECT name FROM sqlite_master
477
- WHERE type='table' AND name NOT LIKE 'sqlite_%'
478
- `);
750
+ await this.detectDatabaseType();
479
751
 
480
- return result.map((row: any) => row.name);
752
+ let query: string;
753
+ let params: any[] = [];
754
+
755
+ switch (this.databaseType) {
756
+ case 'postgresql':
757
+ query = `
758
+ SELECT table_name
759
+ FROM information_schema.tables
760
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
761
+ `;
762
+ break;
763
+ case 'mysql':
764
+ query = `
765
+ SELECT table_name
766
+ FROM information_schema.tables
767
+ WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE'
768
+ `;
769
+ break;
770
+ case 'sqlite':
771
+ default:
772
+ query = `
773
+ SELECT name FROM sqlite_master
774
+ WHERE type='table' AND name NOT LIKE 'sqlite_%'
775
+ `;
776
+ break;
777
+ }
778
+
779
+ const result = await this.db.all(query, params);
780
+ return result.map((row: any) => row.table_name || row.name);
481
781
  }
482
782
 
483
783
  /**
484
784
  * Get all indexes for a table
485
785
  */
486
786
  async getIndexes(tableName: string): Promise<IndexInfo[]> {
487
- const result = await this.db.all(`
488
- PRAGMA index_list("${tableName}")
489
- `);
787
+ await this.detectDatabaseType();
788
+
789
+ let query: string;
790
+ let params: any[] = [tableName];
791
+
792
+ switch (this.databaseType) {
793
+ case 'postgresql':
794
+ query = `
795
+ SELECT
796
+ i.indexname as name,
797
+ array_agg(a.attname ORDER BY a.attnum) as columns,
798
+ i.indexdef LIKE '%UNIQUE%' as unique,
799
+ false as partial
800
+ FROM pg_indexes i
801
+ JOIN pg_class c ON c.relname = i.tablename
802
+ JOIN pg_index ix ON ix.indexrelid = (i.schemaname||'.'||i.indexname)::regclass
803
+ JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = ANY(ix.indkey)
804
+ WHERE i.tablename = $1 AND i.schemaname = 'public'
805
+ GROUP BY i.indexname, i.indexdef
806
+ `;
807
+ break;
808
+ case 'mysql':
809
+ query = `
810
+ SELECT
811
+ INDEX_NAME as name,
812
+ GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) as columns,
813
+ NON_UNIQUE = 0 as unique,
814
+ false as partial
815
+ FROM information_schema.statistics
816
+ WHERE table_schema = DATABASE() AND table_name = ?
817
+ GROUP BY INDEX_NAME, NON_UNIQUE
818
+ `;
819
+ break;
820
+ case 'sqlite':
821
+ default:
822
+ query = `PRAGMA index_list("${tableName}")`;
823
+ params = [];
824
+ break;
825
+ }
826
+
827
+ const result = await this.db.all(query, params);
490
828
 
491
829
  const indexes: IndexInfo[] = [];
492
830
 
493
831
  for (const row of result) {
494
- const indexInfo = await this.db.all(`
495
- PRAGMA index_info("${row.name}")
496
- `);
832
+ let columns: string[];
833
+
834
+ if (this.databaseType === 'sqlite') {
835
+ const indexInfo = await this.db.all(`PRAGMA index_info("${row.name}")`);
836
+ columns = indexInfo.map((col: any) => col.name);
837
+ } else {
838
+ // PostgreSQL and MySQL return columns as comma-separated string
839
+ columns = row.columns.split(',').map((col: string) => col.trim());
840
+ }
497
841
 
498
842
  indexes.push({
499
843
  name: row.name,
500
- columns: indexInfo.map((col: any) => col.name),
501
- unique: row.unique === 1,
502
- partial: row.partial === 1
844
+ columns: columns,
845
+ unique: row.unique === 1 || row.unique === true,
846
+ partial: row.partial === 1 || row.partial === true
503
847
  });
504
848
  }
505
849
 
@@ -510,17 +854,63 @@ export class Schema {
510
854
  * Get all foreign keys for a table
511
855
  */
512
856
  async getForeignKeys(tableName: string): Promise<ForeignKeyInfo[]> {
513
- const result = await this.db.all(`
514
- PRAGMA foreign_key_list("${tableName}")
515
- `);
857
+ await this.detectDatabaseType();
858
+
859
+ let query: string;
860
+ let params: any[] = [tableName];
861
+
862
+ switch (this.databaseType) {
863
+ case 'postgresql':
864
+ query = `
865
+ SELECT
866
+ tc.constraint_name as name,
867
+ kcu.column_name as from_column,
868
+ ccu.table_name as referenced_table,
869
+ ccu.column_name as referenced_column,
870
+ rc.delete_rule as on_delete,
871
+ rc.update_rule as on_update
872
+ FROM information_schema.table_constraints tc
873
+ JOIN information_schema.key_column_usage kcu
874
+ ON tc.constraint_name = kcu.constraint_name
875
+ JOIN information_schema.constraint_column_usage ccu
876
+ ON ccu.constraint_name = tc.constraint_name
877
+ JOIN information_schema.referential_constraints rc
878
+ ON tc.constraint_name = rc.constraint_name
879
+ WHERE tc.constraint_type = 'FOREIGN KEY'
880
+ AND tc.table_name = ?
881
+ `;
882
+ break;
883
+ case 'mysql':
884
+ query = `
885
+ SELECT
886
+ CONSTRAINT_NAME as name,
887
+ COLUMN_NAME as from_column,
888
+ REFERENCED_TABLE_NAME as referenced_table,
889
+ REFERENCED_COLUMN_NAME as referenced_column,
890
+ DELETE_RULE as on_delete,
891
+ UPDATE_RULE as on_update
892
+ FROM information_schema.key_column_usage
893
+ WHERE table_schema = DATABASE()
894
+ AND table_name = ?
895
+ AND REFERENCED_TABLE_NAME IS NOT NULL
896
+ `;
897
+ break;
898
+ case 'sqlite':
899
+ default:
900
+ query = `PRAGMA foreign_key_list("${tableName}")`;
901
+ params = [];
902
+ break;
903
+ }
904
+
905
+ const result = await this.db.all(query, params);
516
906
 
517
907
  return result.map((row: any) => ({
518
- name: row.id.toString(), // SQLite doesn't provide constraint names
519
- columns: [row.from],
520
- referencedTable: row.table,
521
- referencedColumns: [row.to],
522
- onDelete: row.on_delete,
523
- onUpdate: row.on_update
908
+ name: row.name || row.id?.toString() || '',
909
+ columns: [row.from_column || row.from],
910
+ referencedTable: row.referenced_table || row.table,
911
+ referencedColumns: [row.referenced_column || row.to],
912
+ onDelete: row.on_delete || row.on_delete,
913
+ onUpdate: row.on_update || row.on_update
524
914
  }));
525
915
  }
526
916