2014-09-01 17:18:45 +02:00
|
|
|
/*global describe, expect, it*/
|
2016-09-13 18:12:23 -04:00
|
|
|
/*eslint no-var:0, max-len:0 */
|
2014-09-01 17:18:45 +02:00
|
|
|
'use strict';
|
|
|
|
|
2016-05-26 16:56:15 -07:00
|
|
|
var Knex = require('../../../knex');
|
|
|
|
var _ = require('lodash');
|
2016-11-29 10:13:30 +02:00
|
|
|
var Promise = require('bluebird');
|
2013-09-12 01:00:44 -04:00
|
|
|
|
2016-05-26 16:56:15 -07:00
|
|
|
module.exports = function(knex) {
|
2014-04-16 01:23:50 -04:00
|
|
|
|
2013-09-12 13:30:47 -04:00
|
|
|
describe('Additional', function () {
|
2013-09-12 01:00:44 -04:00
|
|
|
|
2017-10-12 18:16:15 +03:00
|
|
|
describe("Custom response processing", () => {
|
|
|
|
|
|
|
|
before('setup custom response handler', () => {
|
|
|
|
knex.client.config.postProcessResponse = (response) => {
|
|
|
|
response.callCount = response.callCount ? (response.callCount + 1) : 1;
|
|
|
|
return response;
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
after('restore client configuration', () => {
|
|
|
|
knex.client.config.postProcessResponse = null;
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should process normal response', () => {
|
|
|
|
return knex('accounts').limit(1).then(res => {
|
|
|
|
expect(res.callCount).to.equal(1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should process raw response', () => {
|
|
|
|
return knex.raw('select * from ??', ['accounts']).then(res => {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should process response done in transaction', () => {
|
|
|
|
return knex.transaction(trx => {
|
|
|
|
return trx('accounts').limit(1).then(res => {
|
|
|
|
expect(res.callCount).to.equal(1);
|
|
|
|
return res;
|
|
|
|
});
|
|
|
|
}).then(res => {
|
|
|
|
expect(res.callCount).to.equal(1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-01-03 00:05:48 +02:00
|
|
|
describe('columnInfo with wrapIdentifier and postProcessResponse', () => {
|
|
|
|
|
|
|
|
before('setup hooks', () => {
|
|
|
|
knex.client.config.postProcessResponse = (response) => {
|
|
|
|
return _.mapKeys(response, (val, key) => {
|
|
|
|
return _.camelCase(key);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
knex.client.config.wrapIdentifier = (id, origImpl) => {
|
|
|
|
return origImpl(_.snakeCase(id));
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
after('restore client configuration', () => {
|
|
|
|
knex.client.config.postProcessResponse = null;
|
|
|
|
knex.client.config.wrapIdentifier = null;
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should work using camelCased table name', () => {
|
|
|
|
return knex('testTableTwo').columnInfo().then(res => {
|
|
|
|
expect(Object.keys(res)).to.eql(['id', 'accountId', 'details', 'status', 'jsonData']);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should work using snake_cased table name', () => {
|
|
|
|
return knex('test_table_two').columnInfo().then(res => {
|
|
|
|
expect(Object.keys(res)).to.eql(['id', 'accountId', 'details', 'status', 'jsonData']);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2017-02-03 18:54:15 -06:00
|
|
|
it('should forward the .get() function from bluebird', function() {
|
|
|
|
return knex('accounts').select().limit(1).then(function(accounts){
|
|
|
|
var firstAccount = accounts[0];
|
|
|
|
return knex('accounts').select().limit(1).get(0).then(function(account){
|
|
|
|
expect(account.id == firstAccount.id);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should forward the .mapSeries() function from bluebird', function() {
|
|
|
|
var asyncTask = function(){
|
|
|
|
return new Promise(function(resolve, reject){
|
|
|
|
var output = asyncTask.num++;
|
|
|
|
setTimeout(function(){
|
|
|
|
resolve(output);
|
|
|
|
}, Math.random()*200);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
asyncTask.num = 1;
|
|
|
|
|
|
|
|
var returnedValues = [];
|
|
|
|
return knex('accounts').select().limit(3).mapSeries(function(account){
|
|
|
|
return asyncTask().then(function(number){
|
|
|
|
returnedValues.push(number);
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.then(function(){
|
|
|
|
expect(returnedValues[0] == 1);
|
|
|
|
expect(returnedValues[1] == 2);
|
|
|
|
expect(returnedValues[2] == 3);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should forward the .delay() function from bluebird', function() {
|
|
|
|
var startTime = (new Date()).valueOf();
|
|
|
|
return knex('accounts').select().limit(1).delay(300).then(function(accounts){
|
|
|
|
expect((new Date()).valueOf() - startTime > 300);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2013-09-12 01:00:44 -04:00
|
|
|
it('should truncate a table with truncate', function() {
|
|
|
|
|
|
|
|
return knex('test_table_two')
|
|
|
|
.truncate()
|
2013-12-27 14:44:21 -05:00
|
|
|
.testSql(function(tester) {
|
|
|
|
tester('mysql', 'truncate `test_table_two`');
|
|
|
|
tester('postgresql', 'truncate "test_table_two" restart identity');
|
2017-05-28 22:48:18 +02:00
|
|
|
tester('sqlite3', "delete from `test_table_two`");
|
2014-08-11 12:25:39 +02:00
|
|
|
tester('oracle', "truncate table \"test_table_two\"");
|
2015-12-09 17:53:53 -06:00
|
|
|
tester('mssql', 'truncate table [test_table_two]');
|
2013-12-27 14:44:21 -05:00
|
|
|
})
|
2017-11-24 08:00:20 +01:00
|
|
|
.then(() => {
|
2013-09-12 01:00:44 -04:00
|
|
|
return knex('test_table_two')
|
|
|
|
.select('*')
|
2017-11-24 08:00:20 +01:00
|
|
|
.then(resp => {
|
2013-09-12 01:00:44 -04:00
|
|
|
expect(resp).to.have.length(0);
|
|
|
|
});
|
2017-11-24 08:00:20 +01:00
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
// Insert new data after truncate and make sure ids restart at 1.
|
|
|
|
// This doesn't currently work on oracle, where the created sequence
|
|
|
|
// needs to be manually reset.
|
|
|
|
if (knex.client.dialect !== 'oracle') {
|
|
|
|
return knex('test_table_two').insert({ status: 1 })
|
|
|
|
.then(res => {
|
|
|
|
return knex('test_table_two')
|
|
|
|
.select('id')
|
|
|
|
.first()
|
|
|
|
.then(res => {
|
|
|
|
expect(res).to.be.an('object')
|
|
|
|
expect(res.id).to.equal(1);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2013-09-12 01:00:44 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should allow raw queries directly with `knex.raw`', function() {
|
|
|
|
var tables = {
|
|
|
|
mysql: 'SHOW TABLES',
|
2014-06-09 16:27:03 -04:00
|
|
|
mysql2: 'SHOW TABLES',
|
2015-04-29 15:14:41 -04:00
|
|
|
mariadb: 'SHOW TABLES',
|
2013-09-12 01:00:44 -04:00
|
|
|
postgresql: "SELECT table_name FROM information_schema.tables WHERE table_schema='public'",
|
2014-08-11 12:25:39 +02:00
|
|
|
sqlite3: "SELECT name FROM sqlite_master WHERE type='table';",
|
2015-12-09 17:53:53 -06:00
|
|
|
oracle: "select TABLE_NAME from USER_TABLES",
|
|
|
|
mssql: "SELECT table_name FROM information_schema.tables WHERE table_schema='dbo'"
|
2013-09-12 01:00:44 -04:00
|
|
|
};
|
2013-12-27 14:44:21 -05:00
|
|
|
return knex.raw(tables[knex.client.dialect]).testSql(function(tester) {
|
|
|
|
tester(knex.client.dialect, tables[knex.client.dialect]);
|
|
|
|
});
|
2013-09-12 01:00:44 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should allow using the primary table as a raw statement', function() {
|
2014-03-14 16:56:55 -04:00
|
|
|
expect(knex(knex.raw("raw_table_name")).toQuery()).to.equal('select * from raw_table_name');
|
2014-04-21 23:08:59 -04:00
|
|
|
});
|
2013-09-12 01:00:44 -04:00
|
|
|
|
2014-07-03 11:59:29 +02:00
|
|
|
it('should allow using .fn-methods to create raw statements', function() {
|
|
|
|
expect(knex.fn.now().prototype === knex.raw().prototype);
|
|
|
|
expect(knex.fn.now().toQuery()).to.equal('CURRENT_TIMESTAMP');
|
|
|
|
});
|
|
|
|
|
2014-04-21 23:08:59 -04:00
|
|
|
it('gets the columnInfo', function() {
|
|
|
|
return knex('datatype_test').columnInfo().testSql(function(tester) {
|
|
|
|
tester('mysql',
|
2014-05-04 23:53:44 -04:00
|
|
|
'select * from information_schema.columns where table_name = ? and table_schema = ?',
|
2014-04-27 18:53:25 -04:00
|
|
|
null, {
|
2014-04-21 23:08:59 -04:00
|
|
|
"enum_value": {
|
2014-05-04 23:53:44 -04:00
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": 1,
|
|
|
|
"nullable": true,
|
2014-04-21 23:08:59 -04:00
|
|
|
"type": "enum"
|
|
|
|
},
|
|
|
|
"uuid": {
|
2014-05-04 23:53:44 -04:00
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": 36,
|
|
|
|
"nullable": false,
|
2014-04-21 23:08:59 -04:00
|
|
|
"type": "char"
|
|
|
|
}
|
|
|
|
});
|
2015-08-09 20:48:33 -03:00
|
|
|
tester('postgresql', 'select * from information_schema.columns where table_name = ? and table_catalog = ? and table_schema = current_schema',
|
2014-04-27 18:53:25 -04:00
|
|
|
null, {
|
2014-04-21 23:08:59 -04:00
|
|
|
"enum_value": {
|
2014-05-04 23:53:44 -04:00
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": null,
|
|
|
|
"nullable": true,
|
2014-04-21 23:08:59 -04:00
|
|
|
"type": "text"
|
|
|
|
},
|
|
|
|
"uuid": {
|
2014-05-04 23:53:44 -04:00
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": null,
|
|
|
|
"nullable": false,
|
2014-04-21 23:08:59 -04:00
|
|
|
"type": "uuid"
|
|
|
|
}
|
|
|
|
});
|
2017-10-23 09:13:09 +03:00
|
|
|
tester('sqlite3', 'PRAGMA table_info(\`datatype_test\`)', [], {
|
2014-04-21 23:08:59 -04:00
|
|
|
"enum_value": {
|
2014-05-04 23:53:44 -04:00
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": null,
|
|
|
|
"nullable": true,
|
2017-05-10 11:04:13 -06:00
|
|
|
"type": "text"
|
2014-04-21 23:08:59 -04:00
|
|
|
},
|
|
|
|
"uuid": {
|
2014-05-04 23:53:44 -04:00
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": "36",
|
|
|
|
"nullable": false,
|
2014-04-21 23:08:59 -04:00
|
|
|
"type": "char"
|
|
|
|
}
|
|
|
|
});
|
2014-08-11 12:25:39 +02:00
|
|
|
tester(
|
|
|
|
'oracle',
|
|
|
|
"select COLUMN_NAME, DATA_TYPE, CHAR_COL_DECL_LENGTH, NULLABLE from USER_TAB_COLS where TABLE_NAME = :1",
|
|
|
|
['datatype_test'],
|
|
|
|
{
|
|
|
|
"enum_value": {
|
|
|
|
nullable: true,
|
|
|
|
maxLength: 1,
|
|
|
|
type: "VARCHAR2"
|
|
|
|
},
|
|
|
|
"uuid": {
|
|
|
|
nullable: false,
|
|
|
|
maxLength: 36,
|
|
|
|
type: "CHAR"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
2015-12-09 17:53:53 -06:00
|
|
|
tester('mssql',
|
2016-07-26 09:00:57 +01:00
|
|
|
'select * from information_schema.columns where table_name = ? and table_catalog = ? and table_schema = \'dbo\'',
|
|
|
|
['datatype_test', 'knex_test'], {
|
2015-12-09 17:53:53 -06:00
|
|
|
"enum_value": {
|
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": 100,
|
|
|
|
"nullable": true,
|
|
|
|
"type": "nvarchar"
|
|
|
|
},
|
|
|
|
"uuid": {
|
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": null,
|
|
|
|
"nullable": false,
|
|
|
|
"type": "uniqueidentifier"
|
|
|
|
}
|
|
|
|
});
|
2014-04-21 23:08:59 -04:00
|
|
|
});
|
2013-09-12 01:00:44 -04:00
|
|
|
});
|
|
|
|
|
2014-06-03 14:21:31 -04:00
|
|
|
it('gets the columnInfo', function() {
|
2014-08-11 12:25:39 +02:00
|
|
|
return knex('datatype_test').columnInfo('uuid').testSql(function(tester) {
|
|
|
|
tester('mysql',
|
|
|
|
'select * from information_schema.columns where table_name = ? and table_schema = ?',
|
|
|
|
null, {
|
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": 36,
|
|
|
|
"nullable": false,
|
|
|
|
"type": "char"
|
2014-06-03 14:21:31 -04:00
|
|
|
});
|
2015-08-09 20:48:33 -03:00
|
|
|
tester('postgresql', 'select * from information_schema.columns where table_name = ? and table_catalog = ? and table_schema = current_schema',
|
2014-08-11 12:25:39 +02:00
|
|
|
null, {
|
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": null,
|
|
|
|
"nullable": false,
|
|
|
|
"type": "uuid"
|
|
|
|
});
|
2017-10-23 09:13:09 +03:00
|
|
|
tester('sqlite3', 'PRAGMA table_info(\`datatype_test\`)', [], {
|
2014-08-11 12:25:39 +02:00
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": "36",
|
|
|
|
"nullable": false,
|
|
|
|
"type": "char"
|
2014-06-03 14:21:31 -04:00
|
|
|
});
|
2014-08-11 12:25:39 +02:00
|
|
|
tester(
|
|
|
|
'oracle',
|
|
|
|
'select COLUMN_NAME, DATA_TYPE, CHAR_COL_DECL_LENGTH, NULLABLE from USER_TAB_COLS where TABLE_NAME = :1',
|
|
|
|
['datatype_test'],
|
|
|
|
{
|
|
|
|
"maxLength": 36,
|
|
|
|
"nullable": false,
|
|
|
|
"type": "CHAR"
|
|
|
|
}
|
|
|
|
);
|
2015-12-09 17:53:53 -06:00
|
|
|
tester('mssql',
|
2016-07-26 09:00:57 +01:00
|
|
|
'select * from information_schema.columns where table_name = ? and table_catalog = ? and table_schema = \'dbo\'',
|
2015-12-09 17:53:53 -06:00
|
|
|
null, {
|
|
|
|
"defaultValue": null,
|
|
|
|
"maxLength": null,
|
|
|
|
"nullable": false,
|
|
|
|
"type": "uniqueidentifier"
|
|
|
|
});
|
2014-08-11 12:25:39 +02:00
|
|
|
});
|
|
|
|
});
|
2014-06-03 14:21:31 -04:00
|
|
|
|
2017-10-23 09:13:09 +03:00
|
|
|
it('#2184 - should properly escape table name for SQLite columnInfo', function() {
|
|
|
|
if (knex.client.dialect !== 'sqlite3') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return knex.schema.dropTableIfExists('group')
|
|
|
|
.then(function() {
|
|
|
|
return knex.schema.createTable('group', function(table) {
|
|
|
|
table.integer('foo');
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.then(function() {
|
|
|
|
return knex('group').columnInfo();
|
|
|
|
})
|
|
|
|
.then(function(columnInfo) {
|
|
|
|
expect(columnInfo).to.deep.equal({
|
|
|
|
foo: {
|
|
|
|
type: 'integer',
|
|
|
|
maxLength: null,
|
|
|
|
nullable: true,
|
|
|
|
defaultValue: null,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-04-16 01:23:50 -04:00
|
|
|
it('should allow renaming a column', function() {
|
2015-12-09 17:53:53 -06:00
|
|
|
var countColumn
|
|
|
|
switch (knex.client.dialect) {
|
|
|
|
case 'oracle': countColumn = 'COUNT(*)'; break;
|
|
|
|
case 'mssql': countColumn = ''; break;
|
|
|
|
default: countColumn = 'count(*)'; break;
|
|
|
|
}
|
2014-04-16 01:23:50 -04:00
|
|
|
var count, inserts = [];
|
2015-12-09 17:53:53 -06:00
|
|
|
_.times(40, function(i) {
|
|
|
|
inserts.push({email: 'email'+ i, first_name: 'Test', last_name: 'Data'});
|
2014-04-16 01:23:50 -04:00
|
|
|
});
|
|
|
|
return knex('accounts').insert(inserts).then(function() {
|
|
|
|
return knex.count('*').from('accounts');
|
|
|
|
}).then(function(resp) {
|
2015-12-09 17:53:53 -06:00
|
|
|
count = resp[0][countColumn];
|
2014-04-16 01:23:50 -04:00
|
|
|
return knex.schema.table('accounts', function(t) {
|
|
|
|
t.renameColumn('about', 'about_col');
|
|
|
|
}).testSql(function(tester) {
|
|
|
|
tester('mysql', ["show fields from `accounts` where field = ?"]);
|
|
|
|
tester('postgresql', ["alter table \"accounts\" rename \"about\" to \"about_col\""]);
|
2017-05-28 22:48:18 +02:00
|
|
|
tester('sqlite3', ["PRAGMA table_info(`accounts`)"]);
|
2014-08-11 12:25:39 +02:00
|
|
|
tester('oracle', ["alter table \"accounts\" rename column \"about\" to \"about_col\""]);
|
2015-12-09 17:53:53 -06:00
|
|
|
tester('mssql', ["exec sp_rename ?, ?, 'COLUMN'"]);
|
2014-04-16 01:23:50 -04:00
|
|
|
});
|
2013-09-12 01:00:44 -04:00
|
|
|
}).then(function() {
|
2014-04-16 01:23:50 -04:00
|
|
|
return knex.count('*').from('accounts');
|
2013-09-12 01:00:44 -04:00
|
|
|
}).then(function(resp) {
|
2015-12-09 17:53:53 -06:00
|
|
|
expect(resp[0][countColumn]).to.equal(count);
|
2014-04-16 01:23:50 -04:00
|
|
|
}).then(function() {
|
|
|
|
return knex('accounts').select('about_col');
|
|
|
|
}).then(function() {
|
2013-09-12 01:00:44 -04:00
|
|
|
return knex.schema.table('accounts', function(t) {
|
|
|
|
t.renameColumn('about_col', 'about');
|
|
|
|
});
|
2014-04-16 01:23:50 -04:00
|
|
|
}).then(function() {
|
|
|
|
return knex.count('*').from('accounts');
|
|
|
|
}).then(function(resp) {
|
2015-12-09 17:53:53 -06:00
|
|
|
expect(resp[0][countColumn]).to.equal(count);
|
2013-09-12 01:00:44 -04:00
|
|
|
});
|
|
|
|
});
|
2014-06-09 15:28:22 -04:00
|
|
|
|
|
|
|
it('should allow dropping a column', function() {
|
2016-10-14 17:00:39 +02:00
|
|
|
var countColumn;
|
2015-12-09 17:53:53 -06:00
|
|
|
switch (knex.client.dialect) {
|
|
|
|
case 'oracle': countColumn = 'COUNT(*)'; break;
|
|
|
|
case 'mssql': countColumn = ''; break;
|
|
|
|
default: countColumn = 'count(*)'; break;
|
2014-08-11 12:25:39 +02:00
|
|
|
}
|
2015-12-09 17:53:53 -06:00
|
|
|
var count;
|
2014-06-09 15:28:22 -04:00
|
|
|
return knex.count('*').from('accounts').then(function(resp) {
|
2015-12-09 17:53:53 -06:00
|
|
|
count = resp[0][countColumn];
|
2014-09-02 22:56:51 +02:00
|
|
|
}).then(function() {
|
2014-06-09 15:28:22 -04:00
|
|
|
return knex.schema.table('accounts', function(t) {
|
|
|
|
t.dropColumn('first_name');
|
|
|
|
}).testSql(function(tester) {
|
|
|
|
tester('mysql', ["alter table `accounts` drop `first_name`"]);
|
|
|
|
tester('postgresql', ['alter table "accounts" drop column "first_name"']);
|
2017-05-28 22:48:18 +02:00
|
|
|
tester('sqlite3', ["PRAGMA table_info(`accounts`)"]);
|
2015-12-09 17:53:53 -06:00
|
|
|
tester('oracle', ['alter table "accounts" drop ("first_name")']);
|
2016-10-14 17:00:39 +02:00
|
|
|
//tester('oracledb', ['alter table "accounts" drop ("first_name")']);
|
2015-12-14 09:56:53 -06:00
|
|
|
tester('mssql', ["ALTER TABLE [accounts] DROP COLUMN [first_name]"]);
|
2014-06-09 15:28:22 -04:00
|
|
|
});
|
|
|
|
}).then(function() {
|
|
|
|
return knex.select('*').from('accounts').first();
|
|
|
|
}).then(function(resp) {
|
|
|
|
expect(_.keys(resp).sort()).to.eql(["about", "created_at", "email", "id", "last_name", "logins", "phone", "updated_at"]);
|
|
|
|
}).then(function() {
|
|
|
|
return knex.count('*').from('accounts');
|
|
|
|
}).then(function(resp) {
|
2015-12-09 17:53:53 -06:00
|
|
|
expect(resp[0][countColumn]).to.equal(count);
|
2014-06-09 15:28:22 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-02-15 17:06:08 +01:00
|
|
|
|
2016-05-26 11:06:33 -07:00
|
|
|
it('.timeout() should throw TimeoutError', function() {
|
2016-10-14 17:00:39 +02:00
|
|
|
var dialect = knex.client.dialect;
|
2016-05-26 11:06:33 -07:00
|
|
|
if(dialect === 'sqlite3') { return; } //TODO -- No built-in support for sleeps
|
2016-03-08 13:07:50 +01:00
|
|
|
var testQueries = {
|
2016-10-14 17:00:39 +02:00
|
|
|
'postgresql': function() {
|
2016-03-08 13:07:50 +01:00
|
|
|
return knex.raw('SELECT pg_sleep(1)');
|
|
|
|
},
|
|
|
|
'mysql': function() {
|
|
|
|
return knex.raw('SELECT SLEEP(1)');
|
|
|
|
},
|
|
|
|
'mysql2': function() {
|
|
|
|
return knex.raw('SELECT SLEEP(1)');
|
|
|
|
},
|
2016-10-14 17:00:39 +02:00
|
|
|
mariadb: function() {
|
2016-03-08 13:07:50 +01:00
|
|
|
return knex.raw('SELECT SLEEP(1)');
|
|
|
|
},
|
|
|
|
mssql: function() {
|
|
|
|
return knex.raw('WAITFOR DELAY \'00:00:01\'');
|
|
|
|
},
|
|
|
|
oracle: function() {
|
2016-10-14 17:00:39 +02:00
|
|
|
return knex.raw('begin dbms_lock.sleep(1); end;');
|
2016-03-08 13:07:50 +01:00
|
|
|
}
|
|
|
|
};
|
2016-02-15 17:06:08 +01:00
|
|
|
|
2016-03-08 13:07:50 +01:00
|
|
|
if(!testQueries.hasOwnProperty(dialect)) {
|
2016-05-26 11:06:33 -07:00
|
|
|
throw new Error('Missing test query for dialect: ' + dialect);
|
2016-03-08 13:07:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var query = testQueries[dialect]();
|
|
|
|
|
2016-10-14 17:00:39 +02:00
|
|
|
return query.timeout(200)
|
2016-02-15 17:06:08 +01:00
|
|
|
.then(function() {
|
|
|
|
expect(true).to.equal(false);
|
|
|
|
})
|
|
|
|
.catch(function(error) {
|
|
|
|
expect(_.pick(error, 'timeout', 'name', 'message')).to.deep.equal({
|
2016-10-14 17:00:39 +02:00
|
|
|
timeout: 200,
|
2016-03-08 13:07:50 +01:00
|
|
|
name: 'TimeoutError',
|
2016-10-14 17:00:39 +02:00
|
|
|
message: 'Defined query timeout of 200ms exceeded when running query.'
|
2016-02-15 17:06:08 +01:00
|
|
|
});
|
2016-05-26 11:06:33 -07:00
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('.timeout(ms, {cancel: true}) should throw TimeoutError and cancel slow query', function() {
|
2016-10-14 17:00:39 +02:00
|
|
|
var dialect = knex.client.dialect;
|
2016-05-26 11:06:33 -07:00
|
|
|
if(dialect === 'sqlite3') { return; } //TODO -- No built-in support for sleeps
|
|
|
|
|
|
|
|
// There's unexpected behavior caused by knex releasing a connection back
|
|
|
|
// to the pool because of a timeout when a long query is still running.
|
|
|
|
// A subsequent query will acquire the connection (still in-use) and hang
|
|
|
|
// until the first query finishes. Setting a sleep time longer than the
|
|
|
|
// mocha timeout exposes this behavior.
|
|
|
|
var testQueries = {
|
2016-10-14 17:00:39 +02:00
|
|
|
'postgresql': function() {
|
2016-05-26 11:06:33 -07:00
|
|
|
return knex.raw('SELECT pg_sleep(10)');
|
|
|
|
},
|
|
|
|
'mysql': function() {
|
|
|
|
return knex.raw('SELECT SLEEP(10)');
|
|
|
|
},
|
|
|
|
'mysql2': function() {
|
|
|
|
return knex.raw('SELECT SLEEP(10)');
|
|
|
|
},
|
2016-10-14 17:00:39 +02:00
|
|
|
mariadb: function() {
|
2016-05-26 11:06:33 -07:00
|
|
|
return knex.raw('SELECT SLEEP(10)');
|
|
|
|
},
|
|
|
|
mssql: function() {
|
|
|
|
return knex.raw('WAITFOR DELAY \'00:00:10\'');
|
|
|
|
},
|
|
|
|
oracle: function() {
|
2016-10-14 17:00:39 +02:00
|
|
|
return knex.raw('begin dbms_lock.sleep(10); end;');
|
2016-05-26 11:06:33 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if(!testQueries.hasOwnProperty(dialect)) {
|
|
|
|
throw new Error('Missing test query for dialect: ' + dialect);
|
|
|
|
}
|
|
|
|
|
|
|
|
var query = testQueries[dialect]();
|
|
|
|
|
2016-05-26 16:56:15 -07:00
|
|
|
function addTimeout() {
|
2016-10-14 17:00:39 +02:00
|
|
|
return query.timeout(200, {cancel: true});
|
2016-05-26 16:56:15 -07:00
|
|
|
}
|
2016-05-26 11:06:33 -07:00
|
|
|
|
2016-05-26 16:56:15 -07:00
|
|
|
// Only mysql/mariadb query cancelling supported for now
|
2016-05-26 17:10:27 -07:00
|
|
|
if (!_.startsWith(dialect, "mysql") && !_.startsWith(dialect, "maria")) {
|
2016-05-26 11:06:33 -07:00
|
|
|
expect(addTimeout).to.throw("Query cancelling not supported for this dialect");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return addTimeout()
|
|
|
|
.then(function() {
|
|
|
|
expect(true).to.equal(false);
|
|
|
|
})
|
|
|
|
.catch(function(error) {
|
|
|
|
expect(_.pick(error, 'timeout', 'name', 'message')).to.deep.equal({
|
2016-10-14 17:00:39 +02:00
|
|
|
timeout: 200,
|
2016-05-26 11:06:33 -07:00
|
|
|
name: 'TimeoutError',
|
2016-10-14 17:00:39 +02:00
|
|
|
message: 'Defined query timeout of 200ms exceeded when running query.'
|
2016-05-26 11:06:33 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
// Ensure sleep command is removed.
|
|
|
|
// This query will hang if a connection gets released back to the pool
|
2017-10-12 18:16:15 +03:00
|
|
|
// too early.
|
2016-11-29 10:13:30 +02:00
|
|
|
// 50ms delay since killing query doesn't seem to have immediate effect to the process listing
|
|
|
|
return Promise.resolve().then().delay(50)
|
2017-10-12 18:16:15 +03:00
|
|
|
.then(function () {
|
2016-11-29 10:13:30 +02:00
|
|
|
return knex.raw('SHOW PROCESSLIST');
|
|
|
|
})
|
2016-05-26 11:06:33 -07:00
|
|
|
.then(function(results) {
|
|
|
|
var processes = results[0];
|
|
|
|
var sleepProcess = _.find(processes, {Info: 'SELECT SLEEP(10)'});
|
|
|
|
expect(sleepProcess).to.equal(undefined);
|
|
|
|
});
|
2016-02-15 17:06:08 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-02-26 19:51:35 +01:00
|
|
|
|
2016-05-26 16:56:15 -07:00
|
|
|
it('.timeout(ms, {cancel: true}) should throw error if cancellation cannot acquire connection', function() {
|
|
|
|
// Only mysql/mariadb query cancelling supported for now
|
|
|
|
var dialect = knex.client.config.dialect;
|
2016-09-13 18:12:23 -04:00
|
|
|
if (!_.startsWith(dialect, "mysql") && !_.startsWith(dialect, "maria")) {
|
|
|
|
return;
|
|
|
|
}
|
2016-05-26 16:56:15 -07:00
|
|
|
|
|
|
|
//To make this test easier, I'm changing the pool settings to max 1.
|
|
|
|
var knexConfig = _.clone(knex.client.config);
|
|
|
|
knexConfig.pool.min = 0;
|
|
|
|
knexConfig.pool.max = 1;
|
|
|
|
|
|
|
|
var knexDb = new Knex(knexConfig);
|
|
|
|
|
|
|
|
return knexDb.raw('SELECT SLEEP(1)')
|
|
|
|
.timeout(1, {cancel: true})
|
|
|
|
.then(function() {
|
|
|
|
throw new Error("Shouldn't have gotten here.");
|
|
|
|
}, function(error) {
|
|
|
|
expect(_.pick(error, 'timeout', 'name', 'message')).to.deep.equal({
|
|
|
|
timeout: 1,
|
|
|
|
name: 'TimeoutError',
|
|
|
|
message: 'After query timeout of 1ms exceeded, cancelling of query failed.'
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2016-02-26 19:51:35 +01:00
|
|
|
it('Event: query-response', function() {
|
|
|
|
var queryCount = 0;
|
|
|
|
|
2016-03-24 18:59:37 +01:00
|
|
|
var onQueryResponse = function(response, obj, builder) {
|
2016-02-26 19:51:35 +01:00
|
|
|
queryCount++;
|
2016-02-26 20:32:34 +01:00
|
|
|
expect(response).to.be.an('array');
|
2016-02-26 20:01:18 +01:00
|
|
|
expect(obj).to.be.an('object');
|
|
|
|
expect(obj.__knexUid).to.be.a('string');
|
2016-06-17 09:53:54 -07:00
|
|
|
expect(obj.__knexQueryUid).to.be.a('string');
|
2016-02-26 20:01:18 +01:00
|
|
|
expect(builder).to.be.an('object');
|
2016-03-24 18:59:37 +01:00
|
|
|
};
|
|
|
|
knex.on('query-response', onQueryResponse);
|
2016-02-26 19:51:35 +01:00
|
|
|
|
|
|
|
return knex('accounts').select()
|
2016-03-24 18:59:37 +01:00
|
|
|
.on('query-response', onQueryResponse)
|
|
|
|
.then(function() {
|
2016-02-26 19:51:35 +01:00
|
|
|
return knex.transaction(function(tr) {
|
2016-03-24 18:59:37 +01:00
|
|
|
return tr('accounts').select().on('query-response', onQueryResponse); //Transactions should emit the event as well
|
2016-02-26 19:51:35 +01:00
|
|
|
})
|
|
|
|
})
|
2016-03-24 18:59:37 +01:00
|
|
|
.then(function() {
|
2017-10-12 18:16:15 +03:00
|
|
|
knex.removeListener('query-response', onQueryResponse);
|
2016-03-24 18:59:37 +01:00
|
|
|
expect(queryCount).to.equal(4);
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('Event: query-error', function() {
|
|
|
|
var queryCount = 0;
|
|
|
|
var onQueryError = function(error, obj) {
|
|
|
|
queryCount++;
|
|
|
|
expect(obj).to.be.an('object');
|
|
|
|
expect(obj.__knexUid).to.be.a('string');
|
2016-06-17 09:53:54 -07:00
|
|
|
expect(obj.__knexQueryUid).to.be.a('string');
|
2016-03-24 18:59:37 +01:00
|
|
|
expect(error).to.be.an('object');
|
|
|
|
};
|
|
|
|
|
|
|
|
knex.on('query-error', onQueryError);
|
|
|
|
|
|
|
|
return knex.raw('Broken query')
|
|
|
|
.on('query-error', onQueryError)
|
|
|
|
.then(function() {
|
|
|
|
expect(true).to.equal(false); //Should not be resolved
|
|
|
|
})
|
|
|
|
.catch(function() {
|
2017-10-12 18:16:15 +03:00
|
|
|
knex.removeListener('query-error', onQueryError);
|
2016-02-26 19:51:35 +01:00
|
|
|
expect(queryCount).to.equal(2);
|
2016-03-24 18:59:37 +01:00
|
|
|
})
|
2016-02-26 19:51:35 +01:00
|
|
|
});
|
|
|
|
|
2013-09-12 01:00:44 -04:00
|
|
|
});
|
|
|
|
|
2014-08-21 23:39:12 +02:00
|
|
|
};
|