11import { Database as SqliteDatabase , DatabaseOpenOptions } from 'https://deno.land/x/sqlite3@0.8.1/mod.ts' ;
22import { buildAggregateQuery , buildAlterQuery , buildCountWhereQuery , buildDeleteQuery , buildInsertQuery , buildModelFromData , buildSelectQuery , buildTableQuery , buildUpdateQuery , isProvidedTypeValid } from './builder.ts' ;
3- import { DBInvalidData , DBInvalidTable , DBModelNotFound , DBNotFound } from './errors.ts' ;
3+ import { DBError , DBInvalidData , DBInvalidTable , DBModelNotFound , DBNotFound } from './errors.ts' ;
44import { dejsonify , jsonify } from './json.ts' ;
55import { prettyPrintDiff } from './util.ts' ;
66import { basename , join } from 'https://deno.land/std@0.192.0/path/mod.ts' ;
7+ import * as ModelReader from './model-reader.ts' ;
78
89interface OrmOptions {
910 /**
@@ -127,7 +128,7 @@ export type PrimitiveTypes = number | string | boolean;
127128export type DeleteQuery = Partial < SelectQuery > ;
128129
129130export class Model {
130- constructor ( public tableName : string , public columns : TableColumn [ ] ) { }
131+ constructor ( public tableName : string , public columns : TableColumn [ ] , public readonly database : string ) { }
131132}
132133
133134const gitBranch = new TextDecoder ( ) . decode (
@@ -149,8 +150,10 @@ export class SqliteOrm {
149150 private hasChangesSinceBackup = false ;
150151 private backupsEnabled = false ;
151152 private hasModelChanges = false ;
153+ private attachedDatabases : string [ ] = [ ] ;
152154
153155 public models : Record < string , Model > = { } ;
156+
154157 private tempModelData : TableColumn [ ] = [ ] ;
155158 private ignoredColumns : string [ ] = [ ] ;
156159
@@ -185,11 +188,7 @@ export class SqliteOrm {
185188 SqliteOrm . logInfo ( this . opts , 'opening database' ) ;
186189
187190 this . db = new SqliteDatabase ( options . dbPath , options . openOptions ) ;
188- try {
189- this . lastModels = JSON . parse ( Deno . readTextFileSync ( `${ options . dbPath } .model.json` ) ) ;
190- } catch ( _e ) {
191- this . lastModels = { } ;
192- }
191+ this . lastModels = ModelReader . read ( options . dbPath ) ;
193192 }
194193
195194 //#region table logic
@@ -209,7 +208,7 @@ export class SqliteOrm {
209208 } ,
210209 limit : 1 ,
211210 } ,
212- this . models [ table . name ] . tableName ,
211+ this . models [ table . name ] ,
213212 ) ;
214213
215214 const found = this . db . prepare ( query . query ) . get ( ...query . params ) ;
@@ -244,7 +243,7 @@ export class SqliteOrm {
244243 public findMany < T extends SqlTable > ( table : new ( ) => T , query : SelectQuery ) : T [ ] {
245244 if ( this . models [ table . name ] == null ) throw new DBModelNotFound ( table ) ;
246245
247- const builtQuery = buildSelectQuery ( query , this . models [ table . name ] . tableName ) ;
246+ const builtQuery = buildSelectQuery ( query , this . models [ table . name ] ) ;
248247
249248 const data = this . db . prepare ( builtQuery . query ) . all ( ...builtQuery . params ) ;
250249 const parsedAll : T [ ] = [ ] ;
@@ -264,14 +263,14 @@ export class SqliteOrm {
264263 public countWhere < T extends SqlTable > ( table : new ( ) => T , query : WhereClause ) : number {
265264 if ( this . models [ table . name ] == null ) throw new DBModelNotFound ( table ) ;
266265
267- const builtQuery = buildCountWhereQuery ( query , this . models [ table . name ] . tableName ) ;
266+ const builtQuery = buildCountWhereQuery ( query , this . models [ table . name ] ) ;
268267 return this . db . prepare ( builtQuery . query ) . get < { 'COUNT(*)' : number } > ( ...builtQuery . params ) ! [ 'COUNT(*)' ] ;
269268 }
270269
271270 public aggregateSelect < Row extends Array < any > , T extends SqlTable = SqlTable > ( table : new ( ) => T , query : AggregateSelectQuery ) : Row [ ] {
272271 if ( this . models [ table . name ] == null ) throw new DBModelNotFound ( table ) ;
273272
274- const builtQuery = buildAggregateQuery ( query , this . models [ table . name ] . tableName ) ;
273+ const builtQuery = buildAggregateQuery ( query , this . models [ table . name ] ) ;
275274 return this . db . prepare ( builtQuery . query ) . values ( ...builtQuery . params ) ;
276275 }
277276
@@ -304,7 +303,7 @@ export class SqliteOrm {
304303 }
305304
306305 public delete < T extends SqlTable > ( table : new ( ) => T , query : DeleteQuery ) {
307- const built = buildDeleteQuery ( query , this . models [ table . name ] . tableName ) ;
306+ const built = buildDeleteQuery ( query , this . models [ table . name ] ) ;
308307 this . db . exec ( built . query , ...built . params ) ;
309308 this . hasChangesSinceBackup = true ;
310309 }
@@ -382,11 +381,12 @@ export class SqliteOrm {
382381 } ;
383382 }
384383
384+ // todo use an object
385385 /**
386386 * Adds a class to orm models.
387387 * @param tableName name of table in database
388388 */
389- public model ( tableName ?: string ) {
389+ public model ( tableName ?: string , database = 'main' ) {
390390 return ( model : new ( ) => SqlTable ) => {
391391 const tempModel = new model ( ) ;
392392 const hasPrimaryKey = this . tempModelData . find ( ( i ) => i . isPrimaryKey ) != null ;
@@ -428,18 +428,18 @@ export class SqliteOrm {
428428 ) ;
429429 }
430430
431- const builtModel = new Model ( tableName ?? model . name , this . tempModelData ) ;
431+ const builtModel = new Model ( tableName ?? model . name , this . tempModelData , database ) ;
432432 this . models [ model . name ] = builtModel ;
433433 this . tempModelData = [ ] ;
434434
435435 // create table if it doesn't exist
436- const info = this . db . prepare ( `PRAGMA table_info('${ model . name } ')` ) . all ( ) ;
436+ const info = this . db . prepare ( `PRAGMA ${ database } . table_info('${ model . name } ')` ) . all ( ) ;
437437 if ( info . length === 0 ) {
438438 this . hasModelChanges = true ;
439439 this . db . exec ( buildTableQuery ( builtModel ) ) ;
440440 } else {
441441 // add missing columns
442- const info = this . db . prepare ( `PRAGMA table_info('${ model . name } ')` ) . all ( ) ;
442+ const info = this . db . prepare ( `PRAGMA ${ database } . table_info('${ model . name } ')` ) . all ( ) ;
443443 buildAlterQuery ( buildModelFromData ( builtModel , info ) , builtModel ) . forEach ( ( c ) => {
444444 this . hasModelChanges = true ;
445445 this . db . exec ( c ) ;
@@ -452,6 +452,12 @@ export class SqliteOrm {
452452 } else {
453453 const oldCols = this . lastModels [ model . name ] . columns ;
454454 const newCols = builtModel . columns ;
455+ const oldDatabase = this . lastModels [ model . name ] . database ;
456+
457+ if ( oldDatabase != null && oldDatabase !== database ) {
458+ SqliteOrm . logInfo ( this . opts , `database change from ${ oldDatabase } to ${ database } ` ) ;
459+ this . hasModelChanges = true ;
460+ }
455461
456462 for ( const oldCol of oldCols ) {
457463 const newCol = newCols . find ( ( c ) => ( c . mappedTo ?? c . name ) === ( oldCol . mappedTo ?? oldCol . name ) ) ;
@@ -564,11 +570,23 @@ export class SqliteOrm {
564570 }
565571
566572 if ( this . hasModelChanges ) {
567- this . saveModel ( ) ;
568573 this . doBackup ( 'model-changes' ) ;
569574 }
570575
571576 this . hasModelChanges = false ;
577+ this . saveModel ( ) ;
578+ }
579+
580+ public attach ( databasePath : string , name ?: string ) {
581+ if ( name == null ) {
582+ name = basename ( databasePath ) ;
583+ }
584+
585+ if ( this . attachedDatabases . includes ( name ) ) throw new DBError ( `${ databasePath } is already attached` ) ;
586+ this . db . exec ( `ATTACH DATABASE ? AS ?` , databasePath , name ) ;
587+ this . attachedDatabases . push ( name ) ;
588+
589+ SqliteOrm . logInfo ( this . opts , `attached ${ databasePath } as ${ name } ` ) ;
572590 }
573591
574592 //#endregion misc
@@ -676,6 +694,6 @@ export class SqliteOrm {
676694 }
677695
678696 private saveModel ( ) {
679- Deno . writeTextFileSync ( ` ${ this . opts . dbPath } .model.json` , JSON . stringify ( this . models , null , 2 ) ) ;
697+ ModelReader . write ( this . models , this . opts . dbPath ) ;
680698 }
681699}
0 commit comments