diff --git a/lib/client.js b/lib/client.js index 72edd699b..a904168f6 100644 --- a/lib/client.js +++ b/lib/client.js @@ -272,6 +272,9 @@ Client.prototype.getStartupConf = function () { if (params.replication) { data.replication = '' + params.replication } + if (params.statement_timeout) { + data.statement_timeout = String(parseInt(params.statement_timeout, 10)) + } return data } diff --git a/lib/connection-parameters.js b/lib/connection-parameters.js index 2ba409c24..f31f28dd3 100644 --- a/lib/connection-parameters.js +++ b/lib/connection-parameters.js @@ -64,6 +64,7 @@ var ConnectionParameters = function (config) { this.application_name = val('application_name', config, 'PGAPPNAME') this.fallback_application_name = val('fallback_application_name', config, false) + this.statement_timeout = val('statement_timeout', config, false) } // Convert arg to a string, surround in single quotes, and escape single quotes and backslashes diff --git a/lib/defaults.js b/lib/defaults.js index 62f2ad158..7fb11d885 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -60,7 +60,10 @@ module.exports = { application_name: undefined, fallback_application_name: undefined, - parseInputDatesAsUTC: false + parseInputDatesAsUTC: false, + + // max milliseconds any query using this connection will execute for before timing out in error. false=unlimited + statement_timeout: false } var pgTypes = require('pg-types') diff --git a/package.json b/package.json index fe251ab10..eddce4902 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "main": "./lib", "dependencies": { "buffer-writer": "1.0.1", - "packet-reader": "0.3.1", "js-string-escape": "1.0.1", + "packet-reader": "0.3.1", "pg-connection-string": "0.1.3", "pg-pool": "~2.0.3", "pg-types": "~1.12.1", diff --git a/test/integration/client/appname-tests.js b/test/integration/client/appname-tests.js index 4db5297c9..e5883908d 100644 --- a/test/integration/client/appname-tests.js +++ b/test/integration/client/appname-tests.js @@ -7,14 +7,7 @@ var suite = new helper.Suite() var conInfo = helper.config function getConInfo (override) { - var newConInfo = {} - Object.keys(conInfo).forEach(function (k) { - newConInfo[k] = conInfo[k] - }) - Object.keys(override || {}).forEach(function (k) { - newConInfo[k] = override[k] - }) - return newConInfo + return Object.assign({}, conInfo, override ) } function getAppName (conf, cb) { diff --git a/test/integration/client/statement_timeout-tests.js b/test/integration/client/statement_timeout-tests.js new file mode 100644 index 000000000..393e82a19 --- /dev/null +++ b/test/integration/client/statement_timeout-tests.js @@ -0,0 +1,77 @@ +'use strict' +var helper = require('./test-helper') +var Client = helper.Client + +var suite = new helper.Suite() + +var conInfo = helper.config + +function getConInfo (override) { + return Object.assign({}, conInfo, override ) +} + +function getStatementTimeout (conf, cb) { + var client = new Client(conf) + client.connect(assert.success(function () { + client.query('SHOW statement_timeout', assert.success(function (res) { + var statementTimeout = res.rows[0].statement_timeout + cb(statementTimeout) + client.end() + })) + })) +} + +if (!helper.args.native) { // statement_timeout is not supported with the native client + suite.test('No default statement_timeout ', function (done) { + getConInfo() + getStatementTimeout({}, function (res) { + assert.strictEqual(res, '0') // 0 = no timeout + done() + }) + }) + + suite.test('statement_timeout integer is used', function (done) { + var conf = getConInfo({ + 'statement_timeout': 3000 + }) + getStatementTimeout(conf, function (res) { + assert.strictEqual(res, '3s') + done() + }) + }) + + suite.test('statement_timeout float is used', function (done) { + var conf = getConInfo({ + 'statement_timeout': 3000.7 + }) + getStatementTimeout(conf, function (res) { + assert.strictEqual(res, '3s') + done() + }) + }) + + suite.test('statement_timeout string is used', function (done) { + var conf = getConInfo({ + 'statement_timeout': '3000' + }) + getStatementTimeout(conf, function (res) { + assert.strictEqual(res, '3s') + done() + }) + }) + + suite.test('statement_timeout actually cancels long running queries', function (done) { + var conf = getConInfo({ + 'statement_timeout': '10' // 10ms to keep tests running fast + }) + var client = new Client(conf) + client.connect(assert.success(function () { + client.query('SELECT pg_sleep( 1 )', function ( error ) { + client.end() + assert.strictEqual( error.code, '57014' ) // query_cancelled + done() + }) + })) + }) + +} diff --git a/test/unit/connection-parameters/creation-tests.js b/test/unit/connection-parameters/creation-tests.js index a3fa25135..fc9f6521f 100644 --- a/test/unit/connection-parameters/creation-tests.js +++ b/test/unit/connection-parameters/creation-tests.js @@ -22,6 +22,7 @@ var compare = function (actual, expected, type) { assert.equal(actual.host, expected.host, type + ' host') assert.equal(actual.password, expected.password, type + ' password') assert.equal(actual.binary, expected.binary, type + ' binary') + assert.equal(actual.statement_timout, expected.statement_timout, type + ' statement_timeout') } test('ConnectionParameters initializing from defaults', function () { @@ -60,7 +61,8 @@ test('ConnectionParameters initializing from config', function () { host: 'yo', ssl: { asdf: 'blah' - } + }, + statement_timeout: 15000 } var subject = new ConnectionParameters(config) compare(subject, config, 'config')