import type { CountOptions , LockOptions , DeleteOptions , FindOneOptions , FindOptions , IDatabaseDriver , NativeInsertUpdateManyOptions , NativeInsertUpdateOptions , DriverMethodOptions } from './IDatabaseDriver' ; import { EntityManagerType } from './IDatabaseDriver' ; import type { AnyEntity , Dictionary , EntityData , EntityDictionary , EntityMetadata , EntityProperty , FilterQuery , PopulateOptions , Primary } from '../typings' ; import type { MetadataStorage } from '../metadata' ; import type { Connection , QueryResult , Transaction } from '../connections' ; import type { Configuration , ConnectionOptions } from '../utils' ; import { EntityComparator , Utils } from '../utils' ; import type { QueryOrderMap } from '../enums' ; import { QueryOrder , ReferenceType } from '../enums' ; import type { Platform } from '../platforms' ; import type { Collection } from '../entity' ; import { EntityManager } from '../EntityManager' ; import { ValidationError } from '../errors' ; import { DriverException } from '../exceptions' ; export abstract class DatabaseDriver < C extends Connection > implements IDatabaseDriver < C > { [ EntityManagerType ] : EntityManager < this > ; protected readonly connection ! : C ; protected readonly replicas : C [ ] = [ ] ; protected readonly platform ! : Platform ; protected readonly logger = this . config . getLogger ( ) ; protected comparator ! : EntityComparator ; protected metadata ! : MetadataStorage ; protected constructor ( readonly config , protected readonly dependencies ) { } abstract find < T , P extends string = never > ( entityName , where , options ? ) ; abstract findOne < T , P extends string = never > ( entityName , where , options ? ) ; abstract nativeInsert < T > ( entityName , data , options ? ) ; abstract nativeInsertMany < T > ( entityName , data , options ? ) ; abstract nativeUpdate < T > ( entityName , where , data , options ? ) ; async nativeUpdateMany < T > ( entityName , where , data , options ? ) { throw new Error ( `Batch updates are not supported by ${ this . constructor . name } driver` ) ; } abstract nativeDelete < T > ( entityName , where , options ? ) ; abstract count < T > ( entityName , where , options ? ) ; createEntityManager < D extends IDatabaseDriver = IDatabaseDriver > ( useContext ? ) { return new EntityManager ( this . config , this , this . metadata , useContext ) as unknown as EntityManager < D > ; } async aggregate ( entityName , pipeline ) { throw new Error ( `Aggregations are not supported by ${ this . constructor . name } driver` ) ; } async loadFromPivotTable < T , O > ( prop , owners , where ? , orderBy ? , ctx ? , options ? ) { throw new Error ( `${ this . constructor . name } does not use pivot tables` ) ; } async syncCollection < T , O > ( coll , options ? ) { const pk = this . metadata . find ( coll . property . type ) ! . primaryKeys [ 0 ] ; const data = { [ coll . property . name ] : coll . getIdentifiers ( pk ) } as EntityData < T > ; await this . nativeUpdate < T > ( coll . owner . constructor . name , coll . owner . __helper ! . getPrimaryKey ( ) as FilterQuery < T > , data , options ) ; } mapResult < T > ( result , meta , populate = [ ] ) { if ( ! result || ! meta ) { return null ; } return this . comparator . mapResult ( meta . className , result ) ; } async connect ( ) { await this . connection . connect ( ) ; await Promise . all ( this . replicas . map ( replica => replica . connect ( ) ) ) ; return this . connection ; } async reconnect ( ) { await this . close ( true ) ; return this . connect ( ) ; } getConnection ( type = 'write' ) { if ( type === 'write' || this . replicas . length === 0 ) { return this . connection as C ; } const rand = Utils . randomInt ( 0 , this . replicas . length - 1 ) ; return this . replicas [ rand ] as C ; } async close ( force ? ) { await Promise . all ( this . replicas . map ( replica => replica . close ( force ) ) ) ; await this . connection . close ( force ) ; if ( this . config . getCacheAdapter ( ) ?. close ) { await this . config . getCacheAdapter ( ) . close ! ( ) ; } if ( this . config . getResultCacheAdapter ( ) ?. close ) { await this . config . getResultCacheAdapter ( ) . close ! ( ) ; } } getPlatform ( ) { return this . platform ; } setMetadata ( metadata ) { this . metadata = metadata ; this . comparator = new EntityComparator ( this . metadata , this . platform ) ; this . connection . setMetadata ( metadata ) ; this . connection . setPlatform ( this . platform ) ; this . replicas . forEach ( replica => { replica . setMetadata ( metadata ) ; replica . setPlatform ( this . platform ) ; } ) ; } getDependencies ( ) { return this . dependencies ; } async ensureIndexes ( ) { throw new Error ( `${ this . constructor . name } does not use ensureIndexes` ) ; } protected inlineEmbeddables < T > ( meta , data , where ? ) { Object . keys ( data ) . forEach ( k => { if ( Utils . isOperator ( k ) ) { Utils . asArray ( data [ k ] ) . forEach ( payload => this . inlineEmbeddables ( meta , payload , where ) ) ; } } ) ; meta . props . forEach ( prop => { if ( prop . reference === ReferenceType . EMBEDDED && prop . object && ! where && Utils . isObject ( data [ prop . name ] ) ) { return ; } if ( prop . reference === ReferenceType . EMBEDDED && Utils . isObject ( data [ prop . name ] ) ) { const props = prop . embeddedProps ; Object . keys ( data [ prop . name ] ) . forEach ( kk => { const operator = Object . keys ( data [ prop . name ] ) . some ( f => Utils . isOperator ( f ) ) ; if ( operator ) { throw ValidationError . cannotUseOperatorsInsideEmbeddables ( meta . name ! , prop . name , data ) ; } if ( prop . object && where ) { const inline = ( payload , sub , path ) => { if ( sub . reference === ReferenceType . EMBEDDED && Utils . isObject ( payload [ sub . embedded ! [ 1 ] ] ) ) { return Object . keys ( payload [ sub . embedded ! [ 1 ] ] ) . forEach ( kkk => { if ( ! sub . embeddedProps [ kkk ] ) { throw ValidationError . invalidEmbeddableQuery ( meta . className , kkk , sub . type ) ; } inline ( payload [ sub . embedded ! [ 1 ] ] , sub . embeddedProps [ kkk ] , [ ... path , sub . embedded ! [ 1 ] ] ) ; } ) ; } data [ `${ path . join ( '.' ) }.${ sub . embedded ! [ 1 ] }` ] = payload [ sub . embedded ! [ 1 ] ] ; } ; inline ( data [ prop . name ] , props [ kk ] , [ prop . name ] ) ; } else if ( props [ kk ] ) { data [ props [ kk ] . name ] = data [ prop . name ] [ props [ kk ] . embedded ! [ 1 ] ] ; } else { throw ValidationError . invalidEmbeddableQuery ( meta . className , kk , prop . type ) ; } } ) ; delete data [ prop . name ] ; } } ) ; } protected getPivotOrderBy < T > ( prop , orderBy ? ) { if ( ! Utils . isEmpty ( orderBy ) ) { return orderBy ! ; } if ( ! Utils . isEmpty ( prop . orderBy ) ) { return Utils . asArray ( prop . orderBy ) ; } if ( prop . fixedOrder ) { return [ { [ `${ prop . pivotTable }.${ prop . fixedOrderColumn }` ] : QueryOrder . ASC } as QueryOrderMap < T > ] ; } return [ ] ; } protected getPrimaryKeyFields ( entityName ) { const meta = this . metadata . find ( entityName ) ; return meta ? meta . primaryKeys : [ this . config . getNamingStrategy ( ) . referenceColumnName ( ) ] ; } protected getPivotInverseProperty ( prop ) { const pivotMeta = this . metadata . find ( prop . pivotTable ) ! ; const targetType = prop . targetMeta ?. root . className ; let inverse ; if ( prop . owner ) { const pivotProp1 = pivotMeta . properties [ targetType + '_inverse' ] ; inverse = pivotProp1 . mappedBy ; } else { const pivotProp1 = pivotMeta . properties [ targetType + '_owner' ] ; inverse = pivotProp1 . inversedBy ; } return pivotMeta . properties [ inverse ] ; } protected createReplicas ( cb ) { const replicas = this . config . get ( 'replicas' , [ ] ) ! ; const ret = [ ] ; const props = [ 'dbName' , 'clientUrl' , 'host' , 'port' , 'user' , 'password' , 'multipleStatements' , 'pool' , 'name' ] as const ; replicas . forEach ( ( conf ) => { props . forEach ( prop => ( conf [ prop ] as any ) = prop in conf ? conf [ prop ] : this . config . get ( prop ) ) ; ret . push ( cb ( conf as ConnectionOptions ) ) ; } ) ; return ret ; } async lockPessimistic < T extends AnyEntity < T > > ( entity , options ) { throw new Error ( `Pessimistic locks are not supported by ${ this . constructor . name } driver` ) ; } inheritDoc convertException ( exception ) { if ( exception instanceof DriverException ) { return exception ; } return this . platform . getExceptionConverter ( ) . convertException ( exception ) ; } protected rethrow < T > ( promise ) { return promise . catch ( e => { throw this . convertException ( e ) ; } ) ; } }