2021-01-09 17:40:30 +02:00
const { Pool , TimeoutError } = require ( 'tarn' ) ;
const { EventEmitter } = require ( 'events' ) ;
2021-01-31 13:40:13 +03:00
const { promisify } = require ( 'util' ) ;
2021-01-09 17:40:30 +02:00
const { makeEscape } = require ( './util/string' ) ;
const cloneDeep = require ( 'lodash/cloneDeep' ) ;
const defaults = require ( 'lodash/defaults' ) ;
const uniqueId = require ( 'lodash/uniqueId' ) ;
2020-12-28 16:55:08 +02:00
const Runner = require ( './execution/runner' ) ;
const Transaction = require ( './execution/transaction' ) ;
2020-12-27 15:19:47 +02:00
const {
executeQuery ,
enrichQueryObject ,
2020-12-28 16:55:08 +02:00
} = require ( './execution/internal/query-executioner' ) ;
2021-01-09 17:59:53 +02:00
const QueryBuilder = require ( './query/querybuilder' ) ;
const QueryCompiler = require ( './query/querycompiler' ) ;
2019-06-04 00:37:17 +02:00
const SchemaBuilder = require ( './schema/builder' ) ;
const SchemaCompiler = require ( './schema/compiler' ) ;
const TableBuilder = require ( './schema/tablebuilder' ) ;
const TableCompiler = require ( './schema/tablecompiler' ) ;
const ColumnBuilder = require ( './schema/columnbuilder' ) ;
const ColumnCompiler = require ( './schema/columncompiler' ) ;
2020-02-12 23:42:15 +03:00
const { KnexTimeoutError } = require ( './util/timeout' ) ;
2021-01-09 17:40:30 +02:00
const { outputQuery , unwrapRaw } = require ( './formatter/wrappingFormatter' ) ;
const { compileCallback } = require ( './formatter/formatterUtils' ) ;
const Raw = require ( './raw' ) ;
const Ref = require ( './ref' ) ;
const Formatter = require ( './formatter' ) ;
const Logger = require ( './logger' ) ;
const { POOL _CONFIG _OPTIONS } = require ( './constants' ) ;
2018-05-29 17:42:03 +02:00
2018-07-09 08:10:34 -04:00
const debug = require ( 'debug' ) ( 'knex:client' ) ;
2016-09-13 18:12:23 -04:00
2016-03-02 17:07:05 +01:00
// The base client provides the general structure
// for a dialect specific client object.
2016-12-11 17:23:44 +01:00
2021-01-31 13:40:13 +03:00
class Client extends EventEmitter {
constructor ( config = { } ) {
super ( ) ;
this . config = config ;
this . logger = new Logger ( config ) ;
2018-08-29 17:13:16 +02:00
2021-01-31 13:40:13 +03:00
//Client is a required field, so throw error if it's not supplied.
//If 'this.dialect' is set, then this is a 'super()' call, in which case
//'client' does not have to be set as it's already assigned on the client prototype.
2018-07-11 13:01:43 -04:00
2021-01-31 13:40:13 +03:00
if ( this . dialect && ! this . config . client ) {
this . logger . warn (
` Using 'this.dialect' to identify the client is deprecated and support for it will be removed in the future. Please use configuration option 'client' instead. `
) ;
}
const dbClient = this . config . client || this . dialect ;
if ( ! dbClient ) {
throw new Error (
` knex: Required configuration option 'client' is missing. `
) ;
2016-03-02 17:07:05 +01:00
}
2019-06-04 00:37:17 +02:00
2021-01-31 13:40:13 +03:00
if ( config . version ) {
this . version = config . version ;
}
2016-03-02 17:07:05 +01:00
2021-01-31 13:40:13 +03:00
if ( config . connection && config . connection instanceof Function ) {
this . connectionConfigProvider = config . connection ;
this . connectionConfigExpirationChecker = ( ) => true ; // causes the provider to be called on first use
} else {
this . connectionSettings = cloneDeep ( config . connection || { } ) ;
this . connectionConfigExpirationChecker = null ;
}
if ( this . driverName && config . connection ) {
this . initializeDriver ( ) ;
if ( ! config . pool || ( config . pool && config . pool . max !== 0 ) ) {
this . initializePool ( config ) ;
}
}
this . valueForUndefined = this . raw ( 'DEFAULT' ) ;
if ( config . useNullAsDefault ) {
this . valueForUndefined = null ;
}
}
2018-02-01 23:41:01 +01:00
formatter ( builder ) {
2018-07-09 08:10:34 -04:00
return new Formatter ( this , builder ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
queryBuilder ( ) {
2018-07-09 08:10:34 -04:00
return new QueryBuilder ( this ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2021-01-07 23:34:46 +02:00
queryCompiler ( builder , formatter ) {
return new QueryCompiler ( this , builder , formatter ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
schemaBuilder ( ) {
2018-07-09 08:10:34 -04:00
return new SchemaBuilder ( this ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
schemaCompiler ( builder ) {
2018-07-09 08:10:34 -04:00
return new SchemaCompiler ( this , builder ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
tableBuilder ( type , tableName , fn ) {
2018-07-09 08:10:34 -04:00
return new TableBuilder ( this , type , tableName , fn ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
tableCompiler ( tableBuilder ) {
2018-07-09 08:10:34 -04:00
return new TableCompiler ( this , tableBuilder ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
columnBuilder ( tableBuilder , type , args ) {
2018-07-09 08:10:34 -04:00
return new ColumnBuilder ( this , tableBuilder , type , args ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
columnCompiler ( tableBuilder , columnBuilder ) {
2018-07-09 08:10:34 -04:00
return new ColumnCompiler ( this , tableBuilder , columnBuilder ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2017-05-16 18:17:19 +08:00
runner ( builder ) {
2018-07-09 08:10:34 -04:00
return new Runner ( this , builder ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
transaction ( container , config , outerTx ) {
2018-07-09 08:10:34 -04:00
return new Transaction ( this , container , config , outerTx ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
raw ( ) {
2018-07-09 08:10:34 -04:00
return new Raw ( this ) . set ( ... arguments ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2018-05-02 12:31:16 +02:00
ref ( ) {
2018-07-09 08:10:34 -04:00
return new Ref ( this , ... arguments ) ;
2021-01-31 13:40:13 +03:00
}
2020-12-27 15:19:47 +02:00
query ( connection , queryParam ) {
const queryObject = enrichQueryObject ( connection , queryParam , this ) ;
return executeQuery ( connection , queryObject , this ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2020-12-27 15:19:47 +02:00
stream ( connection , queryParam , stream , options ) {
const queryObject = enrichQueryObject ( connection , queryParam , this ) ;
return this . _stream ( connection , queryObject , stream , options ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
prepBindings ( bindings ) {
2016-05-11 21:01:56 +03:00
return bindings ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2017-09-27 13:12:40 +03:00
positionBindings ( sql ) {
return sql ;
2021-01-31 13:40:13 +03:00
}
2017-09-27 13:12:40 +03:00
2018-02-01 23:41:01 +01:00
postProcessResponse ( resp , queryContext ) {
2017-10-12 18:16:15 +03:00
if ( this . config . postProcessResponse ) {
2018-02-01 23:41:01 +01:00
return this . config . postProcessResponse ( resp , queryContext ) ;
2017-10-12 18:16:15 +03:00
}
return resp ;
2021-01-31 13:40:13 +03:00
}
2017-10-12 18:16:15 +03:00
2018-02-01 23:41:01 +01:00
wrapIdentifier ( value , queryContext ) {
2018-07-09 08:10:34 -04:00
return this . customWrapIdentifier (
value ,
this . wrapIdentifierImpl ,
queryContext
) ;
2021-01-31 13:40:13 +03:00
}
2018-01-03 00:05:48 +02:00
2018-02-01 23:41:01 +01:00
customWrapIdentifier ( value , origImpl , queryContext ) {
2017-09-08 14:58:59 +03:00
if ( this . config . wrapIdentifier ) {
2018-02-01 23:41:01 +01:00
return this . config . wrapIdentifier ( value , origImpl , queryContext ) ;
2017-09-08 14:58:59 +03:00
}
2018-01-03 00:05:48 +02:00
return origImpl ( value ) ;
2021-01-31 13:40:13 +03:00
}
2017-09-08 14:58:59 +03:00
wrapIdentifierImpl ( value ) {
2018-07-09 08:10:34 -04:00
return value !== '*' ? ` " ${ value . replace ( /"/g , '""' ) } " ` : '*' ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
initializeDriver ( ) {
2016-03-02 17:07:05 +01:00
try {
2018-07-09 08:10:34 -04:00
this . driver = this . _driver ( ) ;
2016-03-02 17:07:05 +01:00
} catch ( e ) {
2018-10-08 16:04:28 +11:00
const message = ` Knex: run \n $ npm install ${ this . driverName } --save ` ;
this . logger . error ( ` ${ message } \n ${ e . message } \n ${ e . stack } ` ) ;
throw new Error ( ` ${ message } \n ${ e . message } ` ) ;
2016-03-02 17:07:05 +01:00
}
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2017-09-06 14:51:29 -05:00
poolDefaults ( ) {
2018-07-09 08:10:34 -04:00
return { min : 2 , max : 10 , propagateCreateError : true } ;
2021-01-31 13:40:13 +03:00
}
2017-09-06 14:51:29 -05:00
getPoolSettings ( poolConfig ) {
poolConfig = defaults ( { } , poolConfig , this . poolDefaults ( ) ) ;
2018-02-07 11:17:17 +02:00
2018-08-29 17:13:16 +02:00
POOL _CONFIG _OPTIONS . forEach ( ( option ) => {
2018-02-07 11:17:17 +02:00
if ( option in poolConfig ) {
2018-07-09 08:10:34 -04:00
this . logger . warn (
[
` Pool config option " ${ option } " is no longer supported. ` ,
` See https://github.com/Vincit/tarn.js for possible pool config options. ` ,
] . join ( ' ' )
) ;
2017-09-06 14:51:29 -05:00
}
2018-07-09 08:10:34 -04:00
} ) ;
2018-02-07 11:17:17 +02:00
const timeouts = [
this . config . acquireConnectionTimeout || 60000 ,
2018-07-09 08:10:34 -04:00
poolConfig . acquireTimeoutMillis ,
] . filter ( ( timeout ) => timeout !== undefined ) ;
2017-09-06 14:51:29 -05:00
// acquire connection timeout can be set on config or config.pool
// choose the smallest, positive timeout setting and set on poolConfig
poolConfig . acquireTimeoutMillis = Math . min ( ... timeouts ) ;
2019-10-27 17:14:18 +02:00
const updatePoolConnectionSettingsFromProvider = async ( ) => {
if ( ! this . connectionConfigProvider ) {
return ; // static configuration, nothing to update
}
if (
! this . connectionConfigExpirationChecker ||
! this . connectionConfigExpirationChecker ( )
) {
return ; // not expired, reuse existing connection
}
const providerResult = await this . connectionConfigProvider ( ) ;
if ( providerResult . expirationChecker ) {
this . connectionConfigExpirationChecker =
providerResult . expirationChecker ;
delete providerResult . expirationChecker ; // MySQL2 driver warns on receiving extra properties
} else {
this . connectionConfigExpirationChecker = null ;
}
this . connectionSettings = providerResult ;
} ;
2018-02-07 11:17:17 +02:00
return Object . assign ( poolConfig , {
2019-10-27 17:14:18 +02:00
create : async ( ) => {
await updatePoolConnectionSettingsFromProvider ( ) ;
const connection = await this . acquireRawConnection ( ) ;
connection . _ _knexUid = uniqueId ( '__knexUid' ) ;
if ( poolConfig . afterCreate ) {
await promisify ( poolConfig . afterCreate ) ( connection ) ;
}
return connection ;
2018-02-07 11:17:17 +02:00
} ,
destroy : ( connection ) => {
if ( connection !== void 0 ) {
2018-07-09 08:10:34 -04:00
return this . destroyRawConnection ( connection ) ;
2016-03-02 17:07:05 +01:00
}
2016-03-19 19:14:46 +01:00
} ,
2018-02-07 11:17:17 +02:00
validate : ( connection ) => {
if ( connection . _ _knex _ _disposed ) {
2018-07-09 08:10:34 -04:00
this . logger . warn ( ` Connection Error: ${ connection . _ _knex _ _disposed } ` ) ;
return false ;
2018-02-07 11:17:17 +02:00
}
2018-07-09 08:10:34 -04:00
return this . validateConnection ( connection ) ;
} ,
} ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2018-07-02 22:52:32 +02:00
initializePool ( config = this . config ) {
2016-09-13 18:12:23 -04:00
if ( this . pool ) {
2018-07-09 08:10:34 -04:00
this . logger . warn ( 'The pool has already been initialized' ) ;
return ;
2016-09-13 18:12:23 -04:00
}
2017-09-06 14:51:29 -05:00
2019-07-11 14:57:59 +02:00
const tarnPoolConfig = {
... this . getPoolSettings ( config . pool ) ,
} ;
// afterCreate is an internal knex param, tarn.js does not support it
if ( tarnPoolConfig . afterCreate ) {
delete tarnPoolConfig . afterCreate ;
}
this . pool = new Pool ( tarnPoolConfig ) ;
2021-01-31 13:40:13 +03:00
}
2016-09-13 18:12:23 -04:00
validateConnection ( connection ) {
2018-07-09 08:10:34 -04:00
return true ;
2021-01-31 13:40:13 +03:00
}
2016-09-13 18:12:23 -04:00
2016-03-02 17:07:05 +01:00
// Acquire a connection from the pool.
2020-02-26 00:50:24 +03:00
async acquireConnection ( ) {
2017-09-06 14:51:29 -05:00
if ( ! this . pool ) {
2020-02-26 00:50:24 +03:00
throw new Error ( 'Unable to acquire a connection' ) ;
2017-09-06 14:51:29 -05:00
}
2019-06-07 17:30:39 -04:00
try {
2020-02-26 00:50:24 +03:00
const connection = await this . pool . acquire ( ) . promise ;
debug ( 'acquired connection from pool: %s' , connection . _ _knexUid ) ;
return connection ;
} catch ( error ) {
let convertedError = error ;
if ( error instanceof TimeoutError ) {
convertedError = new KnexTimeoutError (
'Knex: Timeout acquiring a connection. The pool is probably full. ' +
'Are you missing a .transacting(trx) call?'
) ;
}
throw convertedError ;
2019-06-07 17:30:39 -04:00
}
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
// Releases a connection back to the connection pool,
// returning a promise resolved when the connection is released.
2016-05-17 01:01:34 +10:00
releaseConnection ( connection ) {
2018-07-09 08:10:34 -04:00
debug ( 'releasing connection to pool: %s' , connection . _ _knexUid ) ;
const didRelease = this . pool . release ( connection ) ;
2018-02-07 11:17:17 +02:00
if ( ! didRelease ) {
2018-07-09 08:10:34 -04:00
debug ( 'pool refused connection: %s' , connection . _ _knexUid ) ;
2018-02-07 11:17:17 +02:00
}
2020-02-26 00:50:24 +03:00
return Promise . resolve ( ) ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
// Destroy the current connection pool for the client.
2021-01-21 20:25:06 +02:00
async destroy ( callback ) {
try {
if ( this . pool && this . pool . destroy ) {
await this . pool . destroy ( ) ;
}
this . pool = undefined ;
2018-02-07 11:17:17 +02:00
2021-01-21 20:25:06 +02:00
if ( typeof callback === 'function' ) {
callback ( ) ;
}
} catch ( err ) {
if ( typeof callback === 'function' ) {
return callback ( err ) ;
}
throw err ;
}
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
// Return the database being used by this client.
2016-05-17 01:01:34 +10:00
database ( ) {
2018-07-09 08:10:34 -04:00
return this . connectionSettings . database ;
2021-01-31 13:40:13 +03:00
}
2016-03-02 17:07:05 +01:00
2016-05-17 01:01:34 +10:00
toString ( ) {
2018-07-09 08:10:34 -04:00
return '[object KnexClient]' ;
2021-01-31 13:40:13 +03:00
}
2016-05-27 14:47:12 -07:00
assertCanCancelQuery ( ) {
if ( ! this . canCancelQuery ) {
2018-07-09 08:10:34 -04:00
throw new Error ( 'Query cancelling not supported for this dialect' ) ;
2016-05-27 14:47:12 -07:00
}
2021-01-31 13:40:13 +03:00
}
2016-05-27 14:47:12 -07:00
2016-05-26 11:06:33 -07:00
cancelQuery ( ) {
2018-07-09 08:10:34 -04:00
throw new Error ( 'Query cancelling not supported for this dialect' ) ;
2021-01-31 13:40:13 +03:00
}
2021-01-07 22:44:03 +02:00
2021-01-09 17:40:30 +02:00
// Formatter part
2021-01-07 22:44:03 +02:00
alias ( first , second ) {
return first + ' as ' + second ;
2021-01-31 13:40:13 +03:00
}
2021-01-09 17:40:30 +02:00
// Checks whether a value is a function... if it is, we compile it
// otherwise we check whether it's a raw
2021-02-03 21:17:20 +02:00
parameter ( value , builder , bindingsHolder ) {
2021-01-09 17:40:30 +02:00
if ( typeof value === 'function' ) {
return outputQuery (
2021-02-03 21:17:20 +02:00
compileCallback ( value , undefined , this , bindingsHolder ) ,
2021-01-09 17:40:30 +02:00
true ,
builder ,
this
) ;
}
2021-02-03 21:17:20 +02:00
return unwrapRaw ( value , true , builder , this , bindingsHolder ) || '?' ;
2021-01-31 13:40:13 +03:00
}
2021-02-03 01:13:16 +02:00
// Turns a list of values into a list of ?'s, joining them with commas unless
// a "joining" value is specified (e.g. ' and ')
2021-02-03 21:17:20 +02:00
parameterize ( values , notSetValue , builder , bindingsHolder ) {
2021-02-03 01:13:16 +02:00
if ( typeof values === 'function' )
2021-02-03 21:17:20 +02:00
return this . parameter ( values , builder , bindingsHolder ) ;
2021-02-03 01:13:16 +02:00
values = Array . isArray ( values ) ? values : [ values ] ;
let str = '' ,
i = - 1 ;
while ( ++ i < values . length ) {
if ( i > 0 ) str += ', ' ;
str += this . parameter (
values [ i ] === undefined ? notSetValue : values [ i ] ,
builder ,
2021-02-03 21:17:20 +02:00
bindingsHolder
2021-02-03 01:13:16 +02:00
) ;
}
return str ;
}
// Formats `values` into a parenthesized list of parameters for a `VALUES`
// clause.
//
// [1, 2] -> '(?, ?)'
// [[1, 2], [3, 4]] -> '((?, ?), (?, ?))'
// knex('table') -> '(select * from "table")'
// knex.raw('select ?', 1) -> '(select ?)'
//
2021-02-03 21:17:20 +02:00
values ( values , builder , bindingsHolder ) {
2021-02-03 01:13:16 +02:00
if ( Array . isArray ( values ) ) {
if ( Array . isArray ( values [ 0 ] ) ) {
return ` ( ${ values
. map (
( value ) =>
2021-02-03 21:17:20 +02:00
` ( ${ this . parameterize (
value ,
undefined ,
builder ,
bindingsHolder
) } ) `
2021-02-03 01:13:16 +02:00
)
. join ( ', ' ) } ) ` ;
}
2021-02-03 21:17:20 +02:00
return ` ( ${ this . parameterize (
values ,
undefined ,
builder ,
bindingsHolder
) } ) ` ;
2021-02-03 01:13:16 +02:00
}
if ( values instanceof Raw ) {
2021-02-03 21:17:20 +02:00
return ` ( ${ this . parameter ( values , builder , bindingsHolder ) } ) ` ;
2021-02-03 01:13:16 +02:00
}
2021-02-03 21:17:20 +02:00
return this . parameter ( values , builder , bindingsHolder ) ;
2021-02-03 01:13:16 +02:00
}
2021-01-31 13:40:13 +03:00
}
Object . assign ( Client . prototype , {
_escapeBinding : makeEscape ( {
escapeString ( str ) {
return ` ' ${ str . replace ( /'/g , "''" ) } ' ` ;
} ,
} ) ,
canCancelQuery : false ,
2018-07-09 08:10:34 -04:00
} ) ;
2016-03-02 17:07:05 +01:00
2019-06-04 00:37:17 +02:00
module . exports = Client ;