rittenhop-dev/versions/5.94.2/node_modules/knex/lib/client.js
2024-09-23 19:40:12 -04:00

476 lines
13 KiB
JavaScript

const { Pool, TimeoutError } = require('tarn');
const { EventEmitter } = require('events');
const { promisify } = require('util');
const { makeEscape } = require('./util/string');
const cloneDeep = require('lodash/cloneDeep');
const defaults = require('lodash/defaults');
const uniqueId = require('lodash/uniqueId');
const Runner = require('./execution/runner');
const Transaction = require('./execution/transaction');
const {
executeQuery,
enrichQueryObject,
} = require('./execution/internal/query-executioner');
const QueryBuilder = require('./query/querybuilder');
const QueryCompiler = require('./query/querycompiler');
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');
const { KnexTimeoutError } = require('./util/timeout');
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');
const ViewBuilder = require('./schema/viewbuilder.js');
const ViewCompiler = require('./schema/viewcompiler.js');
const isPlainObject = require('lodash/isPlainObject');
const debug = require('debug')('knex:client');
// The base client provides the general structure
// for a dialect specific client object.
class Client extends EventEmitter {
constructor(config = {}) {
super();
this.config = config;
this.logger = new Logger(config);
//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.
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.`
);
}
if (config.version) {
this.version = config.version;
}
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;
}
}
formatter(builder) {
return new Formatter(this, builder);
}
queryBuilder() {
return new QueryBuilder(this);
}
queryCompiler(builder, formatter) {
return new QueryCompiler(this, builder, formatter);
}
schemaBuilder() {
return new SchemaBuilder(this);
}
schemaCompiler(builder) {
return new SchemaCompiler(this, builder);
}
tableBuilder(type, tableName, tableNameLike, fn) {
return new TableBuilder(this, type, tableName, tableNameLike, fn);
}
viewBuilder(type, viewBuilder, fn) {
return new ViewBuilder(this, type, viewBuilder, fn);
}
tableCompiler(tableBuilder) {
return new TableCompiler(this, tableBuilder);
}
viewCompiler(viewCompiler) {
return new ViewCompiler(this, viewCompiler);
}
columnBuilder(tableBuilder, type, args) {
return new ColumnBuilder(this, tableBuilder, type, args);
}
columnCompiler(tableBuilder, columnBuilder) {
return new ColumnCompiler(this, tableBuilder, columnBuilder);
}
runner(builder) {
return new Runner(this, builder);
}
transaction(container, config, outerTx) {
return new Transaction(this, container, config, outerTx);
}
raw() {
return new Raw(this).set(...arguments);
}
ref() {
return new Ref(this, ...arguments);
}
query(connection, queryParam) {
const queryObject = enrichQueryObject(connection, queryParam, this);
return executeQuery(connection, queryObject, this);
}
stream(connection, queryParam, stream, options) {
const queryObject = enrichQueryObject(connection, queryParam, this);
return this._stream(connection, queryObject, stream, options);
}
prepBindings(bindings) {
return bindings;
}
positionBindings(sql) {
return sql;
}
postProcessResponse(resp, queryContext) {
if (this.config.postProcessResponse) {
return this.config.postProcessResponse(resp, queryContext);
}
return resp;
}
wrapIdentifier(value, queryContext) {
return this.customWrapIdentifier(
value,
this.wrapIdentifierImpl,
queryContext
);
}
customWrapIdentifier(value, origImpl, queryContext) {
if (this.config.wrapIdentifier) {
return this.config.wrapIdentifier(value, origImpl, queryContext);
}
return origImpl(value);
}
wrapIdentifierImpl(value) {
return value !== '*' ? `"${value.replace(/"/g, '""')}"` : '*';
}
initializeDriver() {
try {
this.driver = this._driver();
} catch (e) {
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}`);
}
}
poolDefaults() {
return { min: 2, max: 10, propagateCreateError: true };
}
getPoolSettings(poolConfig) {
poolConfig = defaults({}, poolConfig, this.poolDefaults());
POOL_CONFIG_OPTIONS.forEach((option) => {
if (option in poolConfig) {
this.logger.warn(
[
`Pool config option "${option}" is no longer supported.`,
`See https://github.com/Vincit/tarn.js for possible pool config options.`,
].join(' ')
);
}
});
const DEFAULT_ACQUIRE_TIMEOUT = 60000;
const timeouts = [
this.config.acquireConnectionTimeout,
poolConfig.acquireTimeoutMillis,
].filter((timeout) => timeout !== undefined);
if (!timeouts.length) {
timeouts.push(DEFAULT_ACQUIRE_TIMEOUT);
}
// 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);
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;
};
return Object.assign(poolConfig, {
create: async () => {
await updatePoolConnectionSettingsFromProvider();
const connection = await this.acquireRawConnection();
connection.__knexUid = uniqueId('__knexUid');
if (poolConfig.afterCreate) {
await promisify(poolConfig.afterCreate)(connection);
}
return connection;
},
destroy: (connection) => {
if (connection !== void 0) {
return this.destroyRawConnection(connection);
}
},
validate: (connection) => {
if (connection.__knex__disposed) {
this.logger.warn(`Connection Error: ${connection.__knex__disposed}`);
return false;
}
return this.validateConnection(connection);
},
});
}
initializePool(config = this.config) {
if (this.pool) {
this.logger.warn('The pool has already been initialized');
return;
}
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);
}
validateConnection(connection) {
return true;
}
// Acquire a connection from the pool.
async acquireConnection() {
if (!this.pool) {
throw new Error('Unable to acquire a connection');
}
try {
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;
}
}
// Releases a connection back to the connection pool,
// returning a promise resolved when the connection is released.
releaseConnection(connection) {
debug('releasing connection to pool: %s', connection.__knexUid);
const didRelease = this.pool.release(connection);
if (!didRelease) {
debug('pool refused connection: %s', connection.__knexUid);
}
return Promise.resolve();
}
// Destroy the current connection pool for the client.
async destroy(callback) {
try {
if (this.pool && this.pool.destroy) {
await this.pool.destroy();
}
this.pool = undefined;
if (typeof callback === 'function') {
callback();
}
} catch (err) {
if (typeof callback === 'function') {
return callback(err);
}
throw err;
}
}
// Return the database being used by this client.
database() {
return this.connectionSettings.database;
}
toString() {
return '[object KnexClient]';
}
assertCanCancelQuery() {
if (!this.canCancelQuery) {
throw new Error('Query cancelling not supported for this dialect');
}
}
cancelQuery() {
throw new Error('Query cancelling not supported for this dialect');
}
// Formatter part
alias(first, second) {
return first + ' as ' + second;
}
// Checks whether a value is a function... if it is, we compile it
// otherwise we check whether it's a raw
parameter(value, builder, bindingsHolder) {
if (typeof value === 'function') {
return outputQuery(
compileCallback(value, undefined, this, bindingsHolder),
true,
builder,
this
);
}
return unwrapRaw(value, true, builder, this, bindingsHolder) || '?';
}
// Turns a list of values into a list of ?'s, joining them with commas unless
// a "joining" value is specified (e.g. ' and ')
parameterize(values, notSetValue, builder, bindingsHolder) {
if (typeof values === 'function')
return this.parameter(values, builder, bindingsHolder);
values = Array.isArray(values) ? values : [values];
let str = '',
i = -1;
while (++i < values.length) {
if (i > 0) str += ', ';
let value = values[i];
// json columns can have object in values.
if (isPlainObject(value)) {
value = JSON.stringify(value);
}
str += this.parameter(
value === undefined ? notSetValue : value,
builder,
bindingsHolder
);
}
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 ?)'
//
values(values, builder, bindingsHolder) {
if (Array.isArray(values)) {
if (Array.isArray(values[0])) {
return `(${values
.map(
(value) =>
`(${this.parameterize(
value,
undefined,
builder,
bindingsHolder
)})`
)
.join(', ')})`;
}
return `(${this.parameterize(
values,
undefined,
builder,
bindingsHolder
)})`;
}
if (values && values.isRawInstance) {
return `(${this.parameter(values, builder, bindingsHolder)})`;
}
return this.parameter(values, builder, bindingsHolder);
}
processPassedConnection(connection) {
// Default implementation is noop
}
toPathForJson(jsonPath) {
// By default, we want a json path, so if this function is not overriden,
// we return the path.
return jsonPath;
}
}
Object.assign(Client.prototype, {
_escapeBinding: makeEscape({
escapeString(str) {
return `'${str.replace(/'/g, "''")}'`;
},
}),
canCancelQuery: false,
});
module.exports = Client;