Skip to content

Commit

Permalink
#2758: Implement fail-fast logic for dialect resolution (#2776)
Browse files Browse the repository at this point in the history
* Implement fail-fast logic for dialect resolution, clean-up code around.

* Remove method that was deprecated long time ago

* Address additional comments

* Try addressing comments

* Set client explicitly

* Fix compatibility with older Node versions
  • Loading branch information
kibertoad authored and elhigu committed Aug 29, 2018
1 parent 91f23bc commit 89d2b3a
Show file tree
Hide file tree
Showing 16 changed files with 203 additions and 112 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@

- Use datetime2 for MSSQL datetime + timestamp types. This change is incompatible with MSSQL older than 2008 #2757
- Introduced support for specifying multiple directories for the Migrator #2735
- Knex.VERSION() method was removed, run "require('knex/package').version" instead #2776

# 0.15.2 - 19 Jul, 2018

Expand Down
22 changes: 10 additions & 12 deletions src/client.js
Expand Up @@ -28,6 +28,7 @@ import Logger from './logger';
const debug = require('debug')('knex:client');
const debugQuery = require('debug')('knex:query');
const debugBindings = require('debug')('knex:bindings');
const { POOL_CONFIG_OPTIONS } = require('./constants');

// The base client provides the general structure
// for a dialect specific client object.
Expand All @@ -38,7 +39,14 @@ function Client(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.config.client && !this.dialect) {

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.`);
}

Expand Down Expand Up @@ -221,17 +229,7 @@ assign(Client.prototype, {
getPoolSettings(poolConfig) {
poolConfig = defaults({}, poolConfig, this.poolDefaults());

[
'maxWaitingClients',
'testOnBorrow',
'fifo',
'priorityRange',
'autostart',
'evictionRunIntervalMillis',
'numTestsPerRun',
'softIdleTimeoutMillis',
'Promise',
].forEach((option) => {
POOL_CONFIG_OPTIONS.forEach((option) => {
if (option in poolConfig) {
this.logger.warn(
[
Expand Down
38 changes: 38 additions & 0 deletions src/constants.js
@@ -0,0 +1,38 @@
const { keys } = require('lodash');

// The client names we'll allow in the `{name: lib}` pairing.
const CLIENT_ALIASES = Object.freeze({
pg: 'postgres',
postgresql: 'postgres',
sqlite: 'sqlite3',
});

const SUPPORTED_CLIENTS = Object.freeze(
[
'mssql',
'mysql',
'mysql2',
'oracledb',
'postgres',
'redshift',
'sqlite3',
].concat(keys(CLIENT_ALIASES))
);

const POOL_CONFIG_OPTIONS = Object.freeze([
'maxWaitingClients',
'testOnBorrow',
'fifo',
'priorityRange',
'autostart',
'evictionRunIntervalMillis',
'numTestsPerRun',
'softIdleTimeoutMillis',
'Promise',
]);

module.exports = {
CLIENT_ALIASES,
SUPPORTED_CLIENTS,
POOL_CONFIG_OPTIONS,
};
74 changes: 2 additions & 72 deletions src/index.js
@@ -1,73 +1,3 @@
import Raw from './raw';
import Client from './client';
const Knex = require('./knex');

import makeKnex from './util/make-knex';
import parseConnection from './util/parse-connection';

import { assign } from 'lodash';

// The client names we'll allow in the `{name: lib}` pairing.
const aliases = {
pg: 'postgres',
postgresql: 'postgres',
sqlite: 'sqlite3',
};

export default function Knex(config) {
if (typeof config === 'string') {
return new Knex(assign(parseConnection(config), arguments[2]));
}
let Dialect;
if (arguments.length === 0 || (!config.client && !config.dialect)) {
Dialect = Client;
} else if (
typeof config.client === 'function' &&
config.client.prototype instanceof Client
) {
Dialect = config.client;
} else {
const clientName = config.client || config.dialect;
Dialect = require(`./dialects/${aliases[clientName] ||
clientName}/index.js`);
}
if (typeof config.connection === 'string') {
config = assign({}, config, {
connection: parseConnection(config.connection).connection,
});
}
return makeKnex(new Dialect(config));
}

// Expose Client on the main Knex namespace.
Knex.Client = Client;

/* eslint no-console:0 */

Object.defineProperties(Knex, {
VERSION: {
get() {
console.warn(
'Knex.VERSION is deprecated, you can get the module version' +
"by running require('knex/package').version"
);
return '0.12.6';
},
},
Promise: {
get() {
console.warn(
`Knex.Promise is deprecated, either require bluebird or use the global Promise`
);
return require('bluebird');
},
},
});

// Run a "raw" query, though we can't do anything with it other than put
// it in a query statement.
Knex.raw = (sql, bindings) => {
console.warn(
'global Knex.raw is deprecated, use knex.raw (chain off an initialized knex object)'
);
return new Raw().set(sql, bindings);
};
export default Knex;
74 changes: 74 additions & 0 deletions src/knex.js
@@ -0,0 +1,74 @@
import Raw from './raw';
import Client from './client';

import makeKnex from './util/make-knex';
import parseConnection from './util/parse-connection';
import { SUPPORTED_CLIENTS, CLIENT_ALIASES } from './constants';

export default function Knex(config) {
// If config is string, try to parse it
if (typeof config === 'string') {
const parsedConfig = Object.assign(parseConnection(config), arguments[2]);
return new Knex(parsedConfig);
}

let Dialect;
// If user provided no relevant parameters, use generic client
if (arguments.length === 0 || (!config.client && !config.dialect)) {
Dialect = Client;
}

// If user provided Client constructor as a parameter, use it
else if (
typeof config.client === 'function' &&
config.client.prototype instanceof Client
) {
Dialect = config.client;
}

// If neither applies, let's assume user specified name of a client or dialect as a string
else {
const clientName = config.client || config.dialect;
if (!SUPPORTED_CLIENTS.includes(clientName)) {
throw new Error(
`knex: Unknown configuration option 'client' value ${clientName}. Note that it is case-sensitive, check documentation for supported values.`
);
}

Dialect = require(`./dialects/${CLIENT_ALIASES[clientName] ||
clientName}/index.js`);
}

// If config connection parameter is passed as string, try to parse it
if (typeof config.connection === 'string') {
config = Object.assign({}, config, {
connection: parseConnection(config.connection).connection,
});
}
return makeKnex(new Dialect(config));
}

// Expose Client on the main Knex namespace.
Knex.Client = Client;

/* eslint no-console:0 */

Object.defineProperties(Knex, {
Promise: {
get() {
console.warn(
`Knex.Promise is deprecated, either require bluebird or use the global Promise`
);
return require('bluebird');
},
},
});

// Run a "raw" query, though we can't do anything with it other than put
// it in a query statement.
Knex.raw = (sql, bindings) => {
console.warn(
'global Knex.raw is deprecated, use knex.raw (chain off an initialized knex object)'
);
return new Raw().set(sql, bindings);
};
2 changes: 1 addition & 1 deletion test/integration/suite.js
Expand Up @@ -6,7 +6,7 @@ module.exports = function(knex) {
var sinon = require('sinon');

describe(knex.client.dialect + ' | ' + knex.client.driverName, function() {
this.dialect = knex.client.dialect;
this.client = knex.client.dialect;
this.driverName = knex.client.driverName;

after(function() {
Expand Down
2 changes: 1 addition & 1 deletion test/knexfile.js
Expand Up @@ -104,7 +104,7 @@ var testConfigs = {
},

sqlite3: {
dialect: 'sqlite3',
client: 'sqlite3',
connection: testConfig.sqlite3 || {
filename: __dirname + '/test.sqlite3',
},
Expand Down
30 changes: 30 additions & 0 deletions test/unit/knex.js
@@ -0,0 +1,30 @@
const Knex = require('../../lib/index');
const { expect } = require('chai');

describe('knex', () => {
it('throws error on unsupported config client value', () => {
expect(() => {
Knex({
client: 'dummy',
});
}).to.throw(
/Unknown configuration option 'client' value dummy. Note that it is case-sensitive, check documentation for supported values/
);
});

it('accepts supported config client value', () => {
expect(() => {
Knex({
client: 'mysql',
});
}).not.to.throw();
});

it('accepts supported config client value alias', () => {
expect(() => {
Knex({
client: 'sqlite',
});
}).not.to.throw();
});
});
56 changes: 38 additions & 18 deletions test/unit/query/builder.js
Expand Up @@ -11,23 +11,33 @@ var MSSQL_Client = require('../../../lib/dialects/mssql');

// use driverName as key
var clients = {
mysql: new MySQL_Client({}),
pg: new PG_Client({}),
'pg-redshift': new Redshift_Client({}),
oracledb: new Oracledb_Client({}),
sqlite3: new SQLite3_Client({}),
mssql: new MSSQL_Client({}),
mysql: new MySQL_Client({ client: 'mysql' }),
pg: new PG_Client({ client: 'pg' }),
'pg-redshift': new Redshift_Client({ client: 'redshift' }),
oracledb: new Oracledb_Client({ client: 'oracledb' }),
sqlite3: new SQLite3_Client({ client: 'sqlite3' }),
mssql: new MSSQL_Client({ client: 'mssql' }),
};

var useNullAsDefaultConfig = { useNullAsDefault: true };
// use driverName as key
var clientsWithNullAsDefault = {
mysql: new MySQL_Client(useNullAsDefaultConfig),
pg: new PG_Client(useNullAsDefaultConfig),
'pg-redshift': new Redshift_Client(useNullAsDefaultConfig),
oracledb: new Oracledb_Client(useNullAsDefaultConfig),
sqlite3: new SQLite3_Client(useNullAsDefaultConfig),
mssql: new MSSQL_Client(useNullAsDefaultConfig),
mysql: new MySQL_Client(
Object.assign({ client: 'mysql' }, useNullAsDefaultConfig)
),
pg: new PG_Client(Object.assign({ client: 'mysql' }, useNullAsDefaultConfig)),
'pg-redshift': new Redshift_Client(
Object.assign({ client: 'redshift' }, useNullAsDefaultConfig)
),
oracledb: new Oracledb_Client(
Object.assign({ client: 'oracledb' }, useNullAsDefaultConfig)
),
sqlite3: new SQLite3_Client(
Object.assign({ client: 'sqlite3' }, useNullAsDefaultConfig)
),
mssql: new MSSQL_Client(
Object.assign({ client: 'mssql' }, useNullAsDefaultConfig)
),
};

// note: as a workaround, we are using postgres here, since that's using the default " field wrapping
Expand Down Expand Up @@ -110,12 +120,22 @@ describe('Custom identifier wrapping', function() {

// use driverName as key
var clientsWithCustomIdentifierWrapper = {
mysql: new MySQL_Client(customWrapperConfig),
pg: new PG_Client(customWrapperConfig),
'pg-redshift': new Redshift_Client(customWrapperConfig),
oracledb: new Oracledb_Client(customWrapperConfig),
sqlite3: new SQLite3_Client(customWrapperConfig),
mssql: new MSSQL_Client(customWrapperConfig),
mysql: new MySQL_Client(
Object.assign({ client: 'mysql' }, customWrapperConfig)
),
pg: new PG_Client(Object.assign({ client: 'pg' }, customWrapperConfig)),
'pg-redshift': new Redshift_Client(
Object.assign({ client: 'redshift' }, customWrapperConfig)
),
oracledb: new Oracledb_Client(
Object.assign({ client: 'oracledb' }, customWrapperConfig)
),
sqlite3: new SQLite3_Client(
Object.assign({ client: 'sqlite3' }, customWrapperConfig)
),
mssql: new MSSQL_Client(
Object.assign({ client: 'mssql' }, customWrapperConfig)
),
};

it('should use custom wrapper', () => {
Expand Down
2 changes: 1 addition & 1 deletion test/unit/schema/mssql.js
Expand Up @@ -4,7 +4,7 @@

var sinon = require('sinon');
var MSSQL_Client = require('../../../lib/dialects/mssql');
var client = new MSSQL_Client();
var client = new MSSQL_Client({ client: 'mssql' });

describe('MSSQL SchemaBuilder', function() {
var tableSql;
Expand Down
4 changes: 2 additions & 2 deletions test/unit/schema/mysql.js
Expand Up @@ -11,10 +11,10 @@ module.exports = function(dialect) {
var client;
switch (dialect) {
case 'mysql':
client = new MySQL_Client();
client = new MySQL_Client({ client: 'mysql' });
break;
case 'mysql2':
client = new MySQL2_Client();
client = new MySQL2_Client({ client: 'mysql2' });
break;
}

Expand Down
2 changes: 1 addition & 1 deletion test/unit/schema/oracle.js
Expand Up @@ -4,7 +4,7 @@

var sinon = require('sinon');
var Oracle_Client = require('../../../lib/dialects/oracle');
var client = new Oracle_Client({});
var client = new Oracle_Client({ client: 'oracledb' });

describe('Oracle SchemaBuilder', function() {
var tableSql;
Expand Down

0 comments on commit 89d2b3a

Please sign in to comment.