From 7504c2074531a368712efa377a2bd9504e84973d Mon Sep 17 00:00:00 2001 From: Rocky Date: Mon, 27 Mar 2017 22:20:27 -0500 Subject: [PATCH 01/40] Fix README.md (#1250) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6bb0fe0e9..befd73916 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#node-postgres +# node-postgres [![Build Status](https://secure.travis-ci.org/brianc/node-postgres.svg?branch=master)](http://travis-ci.org/brianc/node-postgres) [![Dependency Status](https://david-dm.org/brianc/node-postgres.svg)](https://david-dm.org/brianc/node-postgres) From 71a136488bf99b4ccdb4912902c451c74314d48c Mon Sep 17 00:00:00 2001 From: javiertury Date: Tue, 28 Mar 2017 05:21:04 +0200 Subject: [PATCH 02/40] Improve Readme.md for not so advanced users (#1235) * Improve Readme.md for not so advanced users 1. Add brief description about the 3 possible ways of executing queries: passing the query to a pool, borrowing a client from a pool or obtaining an exclusive client. Give examples for the 3 of them. 2. Use the examples to teach how to reuse a pool in all of your project. This should be helpful for not so advanced users and prevents mistakes. 3. Open a troubleshooting section. * Shrink Troubleshooting and Point to Examples 1. Troubleshooting/FAQ section will only contain a reference to the wiki FAQ. I've already moved the content to the wiki. 2. At the end of "Pooling example" point to the wiki example page. Also indicate that there they can find how to use node-postgres with promises and async/await. I've already created that content in the wiki. --- README.md | 141 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index befd73916..dc39e38ae 100644 --- a/README.md +++ b/README.md @@ -15,46 +15,36 @@ $ npm install pg ## Intro & Examples -### Simple example +There are 3 ways of executing queries -```js -var pg = require('pg'); +1. Passing the query to a pool +2. Borrowing a client from a pool and executing the query with it +3. Obtaining an exclusive client and executing the query with it -// instantiate a new client -// the client will read connection information from -// the same environment variables used by postgres cli tools -var client = new pg.Client(); +It is recommended to pass the query to a pool as often as possible. If that isn't possible, because of long and complex transactions for example, borrow a client from a pool. Just remember to initialize the pool only once in your code so you maximize reusability of connections. -// connect to our database -client.connect(function (err) { - if (err) throw err; +### Why pooling? - // execute a query on our database - client.query('SELECT $1::text as name', ['brianc'], function (err, result) { - if (err) throw err; +If you're working on something like a web application which makes frequent queries you'll want to access the PostgreSQL server through a pool of clients. Why? For one thing, there is ~20-30 millisecond delay (YMMV) when connecting a new client to the PostgreSQL server because of the startup handshake. Furthermore, PostgreSQL can support only a limited number of clients...it depends on the amount of ram on your database server, but generally more than 100 clients at a time is a __very bad thing__. :tm: Additionally, PostgreSQL can only execute 1 query at a time per connected client, so pipelining all queries for all requests through a single, long-lived client will likely introduce a bottleneck into your application if you need high concurrency. - // just print the result to the console - console.log(result.rows[0]); // outputs: { name: 'brianc' } +With that in mind we can imagine a situation where you have a web server which connects and disconnects a new client for every web request or every query (don't do this!). If you get only 1 request at a time everything will seem to work fine, though it will be a touch slower due to the connection overhead. Once you get >100 simultaneous requests your web server will attempt to open 100 connections to the PostgreSQL backend and :boom: you'll run out of memory on the PostgreSQL server, your database will become unresponsive, your app will seem to hang, and everything will break. Boooo! - // disconnect the client - client.end(function (err) { - if (err) throw err; - }); - }); -}); +__Good news__: node-postgres ships with built in client pooling. Client pooling allows your application to use a pool of already connected clients and reuse them for each request to your application. If your app needs to make more queries than there are available clients in the pool the queries will queue instead of overwhelming your database & causing a cascading failure. :thumbsup: -``` +node-postgres uses [pg-pool](https://github.com/brianc/node-pg-pool.git) to manage pooling. It bundles it and exports it for convenience. If you want, you can `require('pg-pool')` and use it directly - it's the same as the constructor exported at `pg.Pool`. -### Client pooling +It's __highly recommended__ you read the documentation for [pg-pool](https://github.com/brianc/node-pg-pool.git). -If you're working on something like a web application which makes frequent queries you'll want to access the PostgreSQL server through a pool of clients. Why? For one thing, there is ~20-30 millisecond delay (YMMV) when connecting a new client to the PostgreSQL server because of the startup handshake. Furthermore, PostgreSQL can support only a limited number of clients...it depends on the amount of ram on your database server, but generally more than 100 clients at a time is a __very bad thing__. :tm: Additionally, PostgreSQL can only execute 1 query at a time per connected client, so pipelining all queries for all requests through a single, long-lived client will likely introduce a bottleneck into your application if you need high concurrency. +[Here is an up & running quickly example](https://github.com/brianc/node-postgres/wiki/Example) -With that in mind we can imagine a situation where you have a web server which connects and disconnects a new client for every web request or every query (don't do this!). If you get only 1 request at a time everything will seem to work fine, though it will be a touch slower due to the connection overhead. Once you get >100 simultaneous requests your web server will attempt to open 100 connections to the PostgreSQL backend and :boom: you'll run out of memory on the PostgreSQL server, your database will become unresponsive, your app will seem to hang, and everything will break. Boooo! +For more information about `config.ssl` check [TLS (SSL) of nodejs](https://nodejs.org/dist/latest-v4.x/docs/api/tls.html) -__Good news__: node-postgres ships with built in client pooling. Client pooling allows your application to use a pool of already connected clients and reuse them for each request to your application. If your app needs to make more queries than there are available clients in the pool the queries will queue instead of overwhelming your database & causing a cascading failure. :thumbsup: +### Pooling example + +Let's create a pool in `./lib/db.js` which will be reused across the whole project ```javascript -var pg = require('pg'); +const pg = require('pg') // create a config to configure both pooling behavior // and client options @@ -70,18 +60,63 @@ var config = { idleTimeoutMillis: 30000, // how long a client is allowed to remain idle before being closed }; - //this initializes a connection pool //it will keep idle connections open for 30 seconds //and set a limit of maximum 10 idle clients -var pool = new pg.Pool(config); +const pool = new pg.Pool(config); -// to run a query we can acquire a client from the pool, -// run a query on the client, and then return the client to the pool +pool.on('error', function (err, client) { + // if an error is encountered by a client while it sits idle in the pool + // the pool itself will emit an error event with both the error and + // the client which emitted the original error + // this is a rare occurrence but can happen if there is a network partition + // between your application and the database, the database restarts, etc. + // and so you might want to handle it and at least log it out + console.error('idle client error', err.message, err.stack) +}) + +//export the query method for passing queries to the pool +module.exports.query = function (text, values, callback) { + console.log('query:', text, values); + return pool.query(text, values, callback); +}; + +// the pool also supports checking out a client for +// multiple operations, such as a transaction +module.exports.connect = function (callback) { + return pool.connect(callback); +}; +``` + +Now if in `./foo.js` you want to pass a query to the pool + +```js +const pool = require('./lib/db'); + +//to run a query we just pass it to the pool +//after we're done nothing has to be taken care of +//we don't have to return any client to the pool or close a connection +pool.query('SELECT $1::int AS number', ['2'], function(err, res) { + if(err) { + return console.error('error running query', err); + } + + console.log('number:', res.rows[0].number); +}); +``` + +Or if in `./bar.js` you want borrow a client from the pool + +```js +const pool = require('./lib/db'); + +//ask for a client from the pool pool.connect(function(err, client, done) { if(err) { return console.error('error fetching client from pool', err); } + + //use the client for executing the query client.query('SELECT $1::int AS number', ['1'], function(err, result) { //call `done(err)` to release the client back to the pool (or destroy it if there is an error) done(err); @@ -93,27 +128,39 @@ pool.connect(function(err, client, done) { //output: 1 }); }); - -pool.on('error', function (err, client) { - // if an error is encountered by a client while it sits idle in the pool - // the pool itself will emit an error event with both the error and - // the client which emitted the original error - // this is a rare occurrence but can happen if there is a network partition - // between your application and the database, the database restarts, etc. - // and so you might want to handle it and at least log it out - console.error('idle client error', err.message, err.stack) -}) ``` -node-postgres uses [pg-pool](https://github.com/brianc/node-pg-pool.git) to manage pooling. It bundles it and exports it for convenience. If you want, you can `require('pg-pool')` and use it directly - it's the same as the constructor exported at `pg.Pool`. +For more examples, including how to use a connection pool with promises and async/await see the [example](https://github.com/brianc/node-postgres/wiki/Example) page in the wiki. -It's __highly recommended__ you read the documentation for [pg-pool](https://github.com/brianc/node-pg-pool.git). +### Obtaining an exclusive client, example +```js +var pg = require('pg'); -[Here is an up & running quickly example](https://github.com/brianc/node-postgres/wiki/Example) +// instantiate a new client +// the client will read connection information from +// the same environment variables used by postgres cli tools +var client = new pg.Client(); +// connect to our database +client.connect(function (err) { + if (err) throw err; -For more information about `config.ssl` check [TLS (SSL) of nodejs](https://nodejs.org/dist/latest-v4.x/docs/api/tls.html) + // execute a query on our database + client.query('SELECT $1::text as name', ['brianc'], function (err, result) { + if (err) throw err; + + // just print the result to the console + console.log(result.rows[0]); // outputs: { name: 'brianc' } + + // disconnect the client + client.end(function (err) { + if (err) throw err; + }); + }); +}); + +``` ## [More Documentation](https://github.com/brianc/node-postgres/wiki) @@ -183,6 +230,10 @@ Information about the testing processes is in the [wiki](https://github.com/bria Open source belongs to all of us, and we're all invited to participate! +## Troubleshooting and FAQ + +The causes and solutions to common errors can be found among the [Frequently Asked Questions(FAQ)](https://github.com/brianc/node-postgres/wiki/FAQ) + ## Support If at all possible when you open an issue please provide From 0e2625b74ee1c0e57df36795846a490ee4df375b Mon Sep 17 00:00:00 2001 From: Kenneth Schnall Date: Mon, 17 Apr 2017 12:43:09 -0400 Subject: [PATCH 03/40] Add semicolons to Pooling example in README.md (#1266) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dc39e38ae..2f9d11beb 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ For more information about `config.ssl` check [TLS (SSL) of nodejs](https://node Let's create a pool in `./lib/db.js` which will be reused across the whole project ```javascript -const pg = require('pg') +const pg = require('pg'); // create a config to configure both pooling behavior // and client options @@ -72,8 +72,8 @@ pool.on('error', function (err, client) { // this is a rare occurrence but can happen if there is a network partition // between your application and the database, the database restarts, etc. // and so you might want to handle it and at least log it out - console.error('idle client error', err.message, err.stack) -}) + console.error('idle client error', err.message, err.stack); +}); //export the query method for passing queries to the pool module.exports.query = function (text, values, callback) { From 4505ae98d9ff2ddf85a2b6d6d4831b1e4729c26e Mon Sep 17 00:00:00 2001 From: Ary Purnomoz Date: Wed, 19 Apr 2017 21:55:56 +0700 Subject: [PATCH 04/40] support ssl params for pg-native (#1169) Make pg-native able to pass sslmode, sslca, sslkey and sslcert params to libpq --- lib/connection-parameters.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/connection-parameters.js b/lib/connection-parameters.js index f5126a644..68658eff0 100644 --- a/lib/connection-parameters.js +++ b/lib/connection-parameters.js @@ -79,6 +79,12 @@ ConnectionParameters.prototype.getLibpqConnectionString = function(cb) { add(params, this, 'application_name'); add(params, this, 'fallback_application_name'); + var ssl = typeof this.ssl === 'object' ? this.ssl : {sslmode: this.ssl}; + add(params, ssl, 'sslmode'); + add(params, ssl, 'sslca'); + add(params, ssl, 'sslkey'); + add(params, ssl, 'sslcert'); + if(this.database) { params.push("dbname='" + this.database + "'"); } From 4f790deb7310a58a1d342836315a77ca2d76a927 Mon Sep 17 00:00:00 2001 From: Kibae Shin Date: Tue, 25 Apr 2017 01:24:30 +0700 Subject: [PATCH 05/40] Support for logical streaming replication (#1271) * Support for logical streaming replication * Wrong compare expr in getLibpqConnectionString * Simplify codes for replication parameter --- lib/client.js | 4 ++++ lib/connection-parameters.js | 4 ++++ lib/connection.js | 3 +++ 3 files changed, 11 insertions(+) diff --git a/lib/client.js b/lib/client.js index 5d365dab5..1bb27ea22 100644 --- a/lib/client.js +++ b/lib/client.js @@ -26,6 +26,7 @@ var Client = function(config) { this.port = this.connectionParameters.port; this.host = this.connectionParameters.host; this.password = this.connectionParameters.password; + this.replication = this.connectionParameters.replication; var c = config || {}; @@ -222,6 +223,9 @@ Client.prototype.getStartupConf = function() { if (appName) { data.application_name = appName; } + if (params.replication) { + data.replication = '' + params.replication; + } return data; }; diff --git a/lib/connection-parameters.js b/lib/connection-parameters.js index 68658eff0..c1c535e98 100644 --- a/lib/connection-parameters.js +++ b/lib/connection-parameters.js @@ -57,6 +57,7 @@ var ConnectionParameters = function(config) { this.binary = val('binary', config); this.ssl = typeof config.ssl === 'undefined' ? useSsl() : config.ssl; this.client_encoding = val("client_encoding", config); + this.replication = val("replication", config); //a domain socket begins with '/' this.isDomainSocket = (!(this.host||'').indexOf('/')); @@ -88,6 +89,9 @@ ConnectionParameters.prototype.getLibpqConnectionString = function(cb) { if(this.database) { params.push("dbname='" + this.database + "'"); } + if(this.replication) { + params.push("replication='" + this.replication + "'"); + } if(this.host) { params.push("host=" + this.host); } diff --git a/lib/connection.js b/lib/connection.js index 59247a7c4..7318287c2 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -425,6 +425,9 @@ Connection.prototype.parseMessage = function(buffer) { case 0x48: //H return this.parseH(buffer, length); + case 0x57: //W + return new Message('replicationStart', length); + case 0x63: //c return new Message('copyDone', length); From 80d136a531e65b22ae0f9e04652bcf524bd015ec Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Mon, 24 Apr 2017 13:33:38 -0500 Subject: [PATCH 06/40] Add test & documentation for replicationStart message --- CHANGELOG.md | 4 ++++ test/unit/connection/inbound-parser-tests.js | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8efbfed6..56b294133 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ For richer information consult the commit log on github with referenced pull req We do not include break-fix version release in this file. +### v6.2.0 + +- Add support for [parsing `replicationStart` messages](https://github.com/brianc/node-postgres/pull/1271/files). + ### v6.1.0 - Add optional callback parameter to the pure JavaScript `client.end` method. The native client already supported this. diff --git a/test/unit/connection/inbound-parser-tests.js b/test/unit/connection/inbound-parser-tests.js index 13e6fd9eb..a9910966b 100644 --- a/test/unit/connection/inbound-parser-tests.js +++ b/test/unit/connection/inbound-parser-tests.js @@ -347,6 +347,13 @@ test('Connection', function() { name: 'portalSuspended' }); }); + + test('parses replication start message', function() { + testForMessage(new Buffer([0x57, 0x00, 0x00, 0x00, 0x04]), { + name: 'replicationStart', + length: 4 + }); + }); }); //since the data message on a stream can randomly divide the incomming @@ -465,5 +472,4 @@ test('split buffer, multiple message parsing', function() { splitAndVerifyTwoMessages(1); }); }); - }); From f42924bf057943d5a79ff02c4d35b18777dc5754 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Mon, 24 Apr 2017 13:34:03 -0500 Subject: [PATCH 07/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5d7b20cca..f22d223ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.1.5", + "version": "6.2.0", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From db5f4ae1ab3b53529fe568f20b21d4f78f06d41d Mon Sep 17 00:00:00 2001 From: Brian C Date: Mon, 15 May 2017 09:36:18 -0500 Subject: [PATCH 08/40] Upgrade packet reader (#1287) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f22d223ed..a3142c4ec 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "main": "./lib", "dependencies": { "buffer-writer": "1.0.1", - "packet-reader": "0.2.0", + "packet-reader": "0.3.1", "pg-connection-string": "0.1.3", "pg-pool": "1.*", "pg-types": "1.*", From 4659d5d75fa283e5c6fdb75dccfaba698581a630 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Mon, 15 May 2017 09:54:09 -0500 Subject: [PATCH 09/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a3142c4ec..dfbdfbb1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.2.0", + "version": "6.2.1", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From ee8193673c551e5f11d46150aa4b989f64fd95c2 Mon Sep 17 00:00:00 2001 From: Sehrope Sarkuni Date: Mon, 15 May 2017 13:19:13 -0400 Subject: [PATCH 10/40] Libpq connection string escaping (#1285) * Fix escaping of libpq connection string properties Fix handlings of libpq connection properties to properly escape single quotes and backslashes. Previously the values were surrounded in single quotes which handled whitespace within the property value, but internal single quotes and backslashes would cause invalid connection strings to be generated. * Update expected output in test to be quoted Update the expect host output in the connection parameter test to expect it to be surrounded by single quotes. * Add test for configs with quotes and backslashes --- lib/connection-parameters.js | 17 +++++++++----- .../connection-parameters/creation-tests.js | 22 ++++++++++++++++--- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lib/connection-parameters.js b/lib/connection-parameters.js index c1c535e98..da0e998d9 100644 --- a/lib/connection-parameters.js +++ b/lib/connection-parameters.js @@ -65,10 +65,15 @@ var ConnectionParameters = function(config) { this.fallback_application_name = val('fallback_application_name', config, false); }; +// Convert arg to a string, surround in single quotes, and escape single quotes and backslashes +var quoteParamValue = function(value) { + return "'" + ('' + value).replace(/\\/g, "\\\\").replace(/'/g, "\\'") + "'"; +}; + var add = function(params, config, paramName) { var value = config[paramName]; if(value) { - params.push(paramName+"='"+value+"'"); + params.push(paramName + "=" + quoteParamValue(value)); } }; @@ -87,23 +92,23 @@ ConnectionParameters.prototype.getLibpqConnectionString = function(cb) { add(params, ssl, 'sslcert'); if(this.database) { - params.push("dbname='" + this.database + "'"); + params.push("dbname=" + quoteParamValue(this.database)); } if(this.replication) { - params.push("replication='" + this.replication + "'"); + params.push("replication=" + quoteParamValue(this.replication)); } if(this.host) { - params.push("host=" + this.host); + params.push("host=" + quoteParamValue(this.host)); } if(this.isDomainSocket) { return cb(null, params.join(' ')); } if(this.client_encoding) { - params.push("client_encoding='" + this.client_encoding + "'"); + params.push("client_encoding=" + quoteParamValue(this.client_encoding)); } dns.lookup(this.host, function(err, address) { if(err) return cb(err, null); - params.push("hostaddr=" + address); + params.push("hostaddr=" + quoteParamValue(address)); return cb(null, params.join(' ')); }); }; diff --git a/test/unit/connection-parameters/creation-tests.js b/test/unit/connection-parameters/creation-tests.js index 33ee7eeeb..85c2bd037 100644 --- a/test/unit/connection-parameters/creation-tests.js +++ b/test/unit/connection-parameters/creation-tests.js @@ -126,7 +126,7 @@ test('libpq connection string building', function() { checkForPart(parts, "user='brian'"); checkForPart(parts, "password='xyz'"); checkForPart(parts, "port='888'"); - checkForPart(parts, "hostaddr=127.0.0.1"); + checkForPart(parts, "hostaddr='127.0.0.1'"); checkForPart(parts, "dbname='bam'"); })); }); @@ -143,7 +143,7 @@ test('libpq connection string building', function() { assert.isNull(err); var parts = constring.split(" "); checkForPart(parts, "user='brian'"); - checkForPart(parts, "hostaddr=127.0.0.1"); + checkForPart(parts, "hostaddr='127.0.0.1'"); })); }); @@ -173,7 +173,23 @@ test('libpq connection string building', function() { assert.isNull(err); var parts = constring.split(" "); checkForPart(parts, "user='brian'"); - checkForPart(parts, "host=/tmp/"); + checkForPart(parts, "host='/tmp/'"); + })); + }); + + test('config contains quotes and backslashes', function() { + var config = { + user: 'not\\brian', + password: 'bad\'chars', + port: 5432, + host: '/tmp/' + }; + var subject = new ConnectionParameters(config); + subject.getLibpqConnectionString(assert.calls(function(err, constring) { + assert.isNull(err); + var parts = constring.split(" "); + checkForPart(parts, "user='not\\\\brian'"); + checkForPart(parts, "password='bad\\'chars'"); })); }); From c32316df776f58b087b7a1d1882f024b089195cc Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Mon, 15 May 2017 12:21:32 -0500 Subject: [PATCH 11/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dfbdfbb1b..30e4719ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.2.1", + "version": "6.2.2", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From e5f0e5d36a91a72dda93c74388ac890fa42b3be0 Mon Sep 17 00:00:00 2001 From: "T.J. Schuck" Date: Wed, 17 May 2017 17:47:07 -0400 Subject: [PATCH 12/40] s/2016/2017/ (#1291) --- README.md | 2 +- lib/client.js | 2 +- lib/connection-parameters.js | 2 +- lib/connection.js | 2 +- lib/defaults.js | 2 +- lib/index.js | 2 +- lib/native/index.js | 2 +- lib/native/query.js | 2 +- lib/native/result.js | 2 +- lib/query.js | 2 +- lib/result.js | 2 +- lib/type-overrides.js | 2 +- lib/utils.js | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 2f9d11beb..679b5796d 100644 --- a/README.md +++ b/README.md @@ -253,7 +253,7 @@ Follow me [@briancarlson](https://twitter.com/briancarlson) to keep up to date. ## License -Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) +Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/client.js b/lib/client.js index 1bb27ea22..a0d7f21c7 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/connection-parameters.js b/lib/connection-parameters.js index da0e998d9..e6efa158e 100644 --- a/lib/connection-parameters.js +++ b/lib/connection-parameters.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/connection.js b/lib/connection.js index 7318287c2..87d4f274b 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/defaults.js b/lib/defaults.js index b99e4a8c5..b94d33f3c 100644 --- a/lib/defaults.js +++ b/lib/defaults.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/index.js b/lib/index.js index b2372cf4f..65e7be3a5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/native/index.js b/lib/native/index.js index db5dfbf51..1e192e8bf 100644 --- a/lib/native/index.js +++ b/lib/native/index.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/native/query.js b/lib/native/query.js index af75d39b7..fcb1eff4f 100644 --- a/lib/native/query.js +++ b/lib/native/query.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/native/result.js b/lib/native/result.js index dc68f76c4..519037316 100644 --- a/lib/native/result.js +++ b/lib/native/result.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/query.js b/lib/query.js index f62786022..4d49dafd3 100644 --- a/lib/query.js +++ b/lib/query.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/result.js b/lib/result.js index 463fbdbe6..093a73c37 100644 --- a/lib/result.js +++ b/lib/result.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/type-overrides.js b/lib/type-overrides.js index e0cf00e6a..905260898 100644 --- a/lib/type-overrides.js +++ b/lib/type-overrides.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the diff --git a/lib/utils.js b/lib/utils.js index 861b7c5b6..82d1aefd5 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2010-2016 Brian Carlson (brian.m.carlson@gmail.com) + * Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com) * All rights reserved. * * This source code is licensed under the MIT license found in the From 4cd56cc4f857a723aa8eb42ee04b312b86bb0f1e Mon Sep 17 00:00:00 2001 From: Raul Ochoa Date: Wed, 24 May 2017 16:04:50 +0200 Subject: [PATCH 13/40] Make pool name consistent on missing config params (#1279) * Going red: using a config object creates two pools when missing some params It should only create a pool in a consistent way, even if some params are not provided in the first place. * Delay the pool name generation to make it consistent between calls * Don't fallback to empty object as config is already defined --- lib/index.js | 2 +- .../single-pool-on-object-config-tests.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/integration/connection-pool/single-pool-on-object-config-tests.js diff --git a/lib/index.js b/lib/index.js index 65e7be3a5..a2c2792f4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -54,7 +54,6 @@ PG.prototype.connect = function(config, callback) { callback = config; config = null; } - var poolName = JSON.stringify(config || {}); if (typeof config == 'string') { config = new ConnectionParameters(config); } @@ -66,6 +65,7 @@ PG.prototype.connect = function(config, callback) { config.idleTimeoutMillis = config.idleTimeoutMillis || config.poolIdleTimeout || defaults.poolIdleTimeout; config.log = config.log || config.poolLog || defaults.poolLog; + var poolName = JSON.stringify(config); this._pools[poolName] = this._pools[poolName] || new this.Pool(config); var pool = this._pools[poolName]; if(!pool.listeners('error').length) { diff --git a/test/integration/connection-pool/single-pool-on-object-config-tests.js b/test/integration/connection-pool/single-pool-on-object-config-tests.js new file mode 100644 index 000000000..a28cbf5c4 --- /dev/null +++ b/test/integration/connection-pool/single-pool-on-object-config-tests.js @@ -0,0 +1,13 @@ +var helper = require(__dirname + "/../test-helper"); +var pg = require(__dirname + "/../../../lib"); + +pg.connect(helper.config, assert.success(function(client, done) { + assert.equal(Object.keys(pg._pools).length, 1); + pg.connect(helper.config, assert.success(function(client2, done2) { + assert.equal(Object.keys(pg._pools).length, 1); + + done(); + done2(); + pg.end(); + })); +})); From 3757ff7300f03798ee1865f1eaf7125abe9cbda4 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Wed, 24 May 2017 09:05:31 -0500 Subject: [PATCH 14/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 30e4719ad..590a91557 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.2.2", + "version": "6.2.3", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From f2b87e02f129a5508eca6ba5ff60f99ba6cdbe58 Mon Sep 17 00:00:00 2001 From: Brian C Date: Wed, 7 Jun 2017 22:58:03 -0500 Subject: [PATCH 15/40] Add client connectionString tests (#1310) * Remove redundant tests * Add client connectionString test Add test to ensure { connectionString } is respected as an argument to the client constructor * Add test for connection string property Also fixed some legacy require statements. --- lib/pool-factory.js | 1 - .../double-connection-tests.js | 2 +- .../ending-empty-pool-tests.js | 2 +- .../connection-pool/ending-pool-tests.js | 3 ++- .../connection-pool/error-tests.js | 4 +-- .../connection-pool/idle-timeout-tests.js | 2 +- .../connection-pool/max-connection-tests.js | 2 +- .../connection-pool/native-instance-tests.js | 2 +- .../connection-pool/optional-config-tests.js | 2 +- .../single-connection-tests.js | 2 +- .../single-pool-on-object-config-tests.js | 4 +-- .../waiting-connection-tests.js | 2 +- test/unit/client/configuration-tests.js | 11 ++++++++ test/unit/client/connection-string-tests.js | 27 ------------------- 14 files changed, 25 insertions(+), 41 deletions(-) delete mode 100644 test/unit/client/connection-string-tests.js diff --git a/lib/pool-factory.js b/lib/pool-factory.js index aa7bd0b19..85f0e6a50 100644 --- a/lib/pool-factory.js +++ b/lib/pool-factory.js @@ -3,7 +3,6 @@ var util = require('util'); var Pool = require('pg-pool'); module.exports = function(Client) { - var BoundPool = function(options) { var config = { Client: Client }; for (var key in options) { diff --git a/test/integration/connection-pool/double-connection-tests.js b/test/integration/connection-pool/double-connection-tests.js index ae7eb3169..421e1f9ea 100644 --- a/test/integration/connection-pool/double-connection-tests.js +++ b/test/integration/connection-pool/double-connection-tests.js @@ -1,2 +1,2 @@ -var helper = require(__dirname + "/test-helper") +var helper = require("./test-helper") helper.testPoolSize(2); diff --git a/test/integration/connection-pool/ending-empty-pool-tests.js b/test/integration/connection-pool/ending-empty-pool-tests.js index 4f5dd80ad..d1acc6f20 100644 --- a/test/integration/connection-pool/ending-empty-pool-tests.js +++ b/test/integration/connection-pool/ending-empty-pool-tests.js @@ -1,4 +1,4 @@ -var helper = require(__dirname + '/test-helper') +var helper = require('./test-helper') var called = false; test('disconnects', function() { diff --git a/test/integration/connection-pool/ending-pool-tests.js b/test/integration/connection-pool/ending-pool-tests.js index 83f4b1bc2..3a1ab46f9 100644 --- a/test/integration/connection-pool/ending-pool-tests.js +++ b/test/integration/connection-pool/ending-pool-tests.js @@ -1,6 +1,7 @@ -var helper = require(__dirname + '/test-helper') +var helper = require('./test-helper') var called = false; + test('disconnects', function() { var sink = new helper.Sink(4, function() { called = true; diff --git a/test/integration/connection-pool/error-tests.js b/test/integration/connection-pool/error-tests.js index 2cf0501fa..59121a86e 100644 --- a/test/integration/connection-pool/error-tests.js +++ b/test/integration/connection-pool/error-tests.js @@ -1,5 +1,5 @@ -var helper = require(__dirname + "/../test-helper"); -var pg = require(__dirname + "/../../../lib"); +var helper = require("../test-helper"); +var pg = require("../../../lib"); //first make pool hold 2 clients pg.defaults.poolSize = 2; diff --git a/test/integration/connection-pool/idle-timeout-tests.js b/test/integration/connection-pool/idle-timeout-tests.js index 0a60ce504..b0908b6fb 100644 --- a/test/integration/connection-pool/idle-timeout-tests.js +++ b/test/integration/connection-pool/idle-timeout-tests.js @@ -1,4 +1,4 @@ -var helper = require(__dirname + '/test-helper'); +var helper = require('./test-helper'); var _ = require('lodash') const config = _.extend({ }, helper.config, { idleTimeoutMillis: 50 }) diff --git a/test/integration/connection-pool/max-connection-tests.js b/test/integration/connection-pool/max-connection-tests.js index 68c0773f9..944d2fb2e 100644 --- a/test/integration/connection-pool/max-connection-tests.js +++ b/test/integration/connection-pool/max-connection-tests.js @@ -1,2 +1,2 @@ -var helper = require(__dirname + "/test-helper") +var helper = require("./test-helper") helper.testPoolSize(40); diff --git a/test/integration/connection-pool/native-instance-tests.js b/test/integration/connection-pool/native-instance-tests.js index 06fbdb45b..314920c4d 100644 --- a/test/integration/connection-pool/native-instance-tests.js +++ b/test/integration/connection-pool/native-instance-tests.js @@ -1,4 +1,4 @@ -var helper = require(__dirname + "/../test-helper") +var helper = require("./../test-helper") var pg = helper.pg var native = helper.args.native diff --git a/test/integration/connection-pool/optional-config-tests.js b/test/integration/connection-pool/optional-config-tests.js index f0ba2e76e..be7063eb8 100644 --- a/test/integration/connection-pool/optional-config-tests.js +++ b/test/integration/connection-pool/optional-config-tests.js @@ -1,4 +1,4 @@ -var helper = require(__dirname + '/test-helper'); +var helper = require('./test-helper'); //setup defaults helper.pg.defaults.user = helper.args.user; diff --git a/test/integration/connection-pool/single-connection-tests.js b/test/integration/connection-pool/single-connection-tests.js index 5ca0a8884..89f6f069e 100644 --- a/test/integration/connection-pool/single-connection-tests.js +++ b/test/integration/connection-pool/single-connection-tests.js @@ -1,2 +1,2 @@ -var helper = require(__dirname + "/test-helper") +var helper = require("./test-helper") helper.testPoolSize(1); diff --git a/test/integration/connection-pool/single-pool-on-object-config-tests.js b/test/integration/connection-pool/single-pool-on-object-config-tests.js index a28cbf5c4..81cdf8e45 100644 --- a/test/integration/connection-pool/single-pool-on-object-config-tests.js +++ b/test/integration/connection-pool/single-pool-on-object-config-tests.js @@ -1,5 +1,5 @@ -var helper = require(__dirname + "/../test-helper"); -var pg = require(__dirname + "/../../../lib"); +var helper = require("../test-helper"); +var pg = require("../../../lib"); pg.connect(helper.config, assert.success(function(client, done) { assert.equal(Object.keys(pg._pools).length, 1); diff --git a/test/integration/connection-pool/waiting-connection-tests.js b/test/integration/connection-pool/waiting-connection-tests.js index f2519ec55..82572d1e4 100644 --- a/test/integration/connection-pool/waiting-connection-tests.js +++ b/test/integration/connection-pool/waiting-connection-tests.js @@ -1,2 +1,2 @@ -var helper = require(__dirname + "/test-helper") +var helper = require("./test-helper") helper.testPoolSize(200); diff --git a/test/unit/client/configuration-tests.js b/test/unit/client/configuration-tests.js index 0204af22b..5548e5fb1 100644 --- a/test/unit/client/configuration-tests.js +++ b/test/unit/client/configuration-tests.js @@ -59,6 +59,17 @@ test('client settings', function() { test('initializing from a config string', function() { + test('uses connectionString property', function () { + var client = new Client({ + connectionString: 'postgres://brian:pass@host1:333/databasename' + }) + assert.equal(client.user, 'brian'); + assert.equal(client.password, "pass"); + assert.equal(client.host, "host1"); + assert.equal(client.port, 333); + assert.equal(client.database, "databasename"); + }) + test('uses the correct values from the config string', function() { var client = new Client("postgres://brian:pass@host1:333/databasename") assert.equal(client.user, 'brian'); diff --git a/test/unit/client/connection-string-tests.js b/test/unit/client/connection-string-tests.js deleted file mode 100644 index 9316daa9b..000000000 --- a/test/unit/client/connection-string-tests.js +++ /dev/null @@ -1,27 +0,0 @@ -require(__dirname + '/test-helper'); - -/* - * Perhaps duplicate of test named 'initializing from a config string' in - * configuration-tests.js - */ - -test("using connection string in client constructor", function() { - var client = new Client("postgres://brian:pw@boom:381/lala"); - - test("parses user", function() { - assert.equal(client.user,'brian'); - }); - test("parses password", function() { - assert.equal(client.password, 'pw'); - }); - test("parses host", function() { - assert.equal(client.host, 'boom'); - }); - test('parses port', function() { - assert.equal(client.port, 381) - }); - test('parses database', function() { - assert.equal(client.database, 'lala') - }); -}); - From 76c59a01f26c06ada75f56b1b455317cd9e2d758 Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Fri, 9 Jun 2017 10:56:02 -0500 Subject: [PATCH 16/40] Emit error when backend unexpectedly disconnects --- lib/client.js | 46 +++++++--- lib/connection.js | 5 -- .../client/error-handling-tests.js | 22 ++++- .../client/network-partition-tests.js | 90 +++++++++++++++++++ ...error-handling-prepared-statement-tests.js | 15 ++-- ...tream-and-query-error-interaction-tests.js | 2 + 6 files changed, 156 insertions(+), 24 deletions(-) create mode 100644 test/integration/client/network-partition-tests.js diff --git a/lib/client.js b/lib/client.js index a0d7f21c7..78d8bd7b9 100644 --- a/lib/client.js +++ b/lib/client.js @@ -31,6 +31,9 @@ var Client = function(config) { var c = config || {}; this._types = new TypeOverrides(c.types); + this._ending = false; + this._connecting = false; + this._connectionError = false; this.connection = c.connection || new Connection({ stream: c.stream, @@ -50,6 +53,7 @@ util.inherits(Client, EventEmitter); Client.prototype.connect = function(callback) { var self = this; var con = this.connection; + this._connecting = true; if(this.host && this.host.indexOf('/') === 0) { con.connect(this.host + '/.s.PGSQL.' + this.port); @@ -107,6 +111,7 @@ Client.prototype.connect = function(callback) { //hook up query handling events to connection //after the connection initially becomes ready for queries con.once('readyForQuery', function() { + self._connecting = false; //delegate rowDescription to active query con.on('rowDescription', function(msg) { @@ -175,34 +180,52 @@ Client.prototype.connect = function(callback) { }); con.on('error', function(error) { - if(self.activeQuery) { + if(this.activeQuery) { var activeQuery = self.activeQuery; - self.activeQuery = null; + this.activeQuery = null; return activeQuery.handleError(error, con); } + + if (this._connecting) { + // set a flag indicating we've seen an error during connection + // the backend will terminate the connection and we don't want + // to throw a second error when the connection is terminated + this._connectionError = true; + } + if(!callback) { - return self.emit('error', error); + return this.emit('error', error); } + con.end(); // make sure ECONNRESET errors don't cause error events callback(error); callback = null; - }); + }.bind(this)); con.once('end', function() { - if ( callback ) { - // haven't received a connection message yet ! + if (callback) { + // haven't received a connection message yet! var err = new Error('Connection terminated'); callback(err); callback = null; return; } - if(self.activeQuery) { + if(this.activeQuery) { var disconnectError = new Error('Connection terminated'); - self.activeQuery.handleError(disconnectError, con); - self.activeQuery = null; + this.activeQuery.handleError(disconnectError, con); + this.activeQuery = null; } - self.emit('end'); - }); + if (!this._ending) { + // if the connection is ended without us calling .end() + // on this client then we have an unexpected disconnection + // treat this as an error unless we've already emitted an error + // during connection. + if (!this._connectionError) { + this.emit('error', new Error('Connection terminated unexpectedly')); + } + } + this.emit('end'); + }.bind(this)); con.on('notice', function(msg) { @@ -342,6 +365,7 @@ Client.prototype.query = function(config, values, callback) { }; Client.prototype.end = function(cb) { + this._ending = true; this.connection.end(); if (cb) { this.connection.once('end', cb); diff --git a/lib/connection.js b/lib/connection.js index 87d4f274b..b031ece16 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -87,9 +87,6 @@ Connection.prototype.connect = function(port, host) { }); this.stream.on('close', function() { - // NOTE: node-0.10 emits both 'end' and 'close' - // for streams closed by the peer, while - // node-0.8 only emits 'close' self.emit('end'); }); @@ -143,8 +140,6 @@ Connection.prototype.attachListeners = function(stream) { }; Connection.prototype.requestSsl = function() { - this.checkSslResponse = true; - var bodyBuffer = this.writer .addInt16(0x04D2) .addInt16(0x162F).flush(); diff --git a/test/integration/client/error-handling-tests.js b/test/integration/client/error-handling-tests.js index d3bf36c29..3fcd305bb 100644 --- a/test/integration/client/error-handling-tests.js +++ b/test/integration/client/error-handling-tests.js @@ -1,6 +1,24 @@ var helper = require(__dirname + '/test-helper'); var util = require('util'); + + test('non-query error with callback', function () { + var client = new Client({ + user:'asldkfjsadlfkj' + }); + client.connect(assert.calls(function (err) { + assert(err); + })); + }); + + test('non-query error', function() { + var client = new Client({ + user:'asldkfjsadlfkj' + }); + assert.emits(client, 'error'); + client.connect(); + }); + var createErorrClient = function() { var client = helper.client(); client.once('error', function(err) { @@ -11,9 +29,8 @@ var createErorrClient = function() { return client; }; -test('error handling', function(){ +test('error handling', function() { test('within a simple query', function() { - var client = createErorrClient(); var query = client.query("select omfg from yodas_dsflsd where pixistix = 'zoiks!!!'"); @@ -77,7 +94,6 @@ test('error handling', function(){ }); test('non-query error', function() { - var client = new Client({ user:'asldkfjsadlfkj' }); diff --git a/test/integration/client/network-partition-tests.js b/test/integration/client/network-partition-tests.js new file mode 100644 index 000000000..944df510c --- /dev/null +++ b/test/integration/client/network-partition-tests.js @@ -0,0 +1,90 @@ +var co = require('co') + +var buffers = require('../../test-buffers') +var helper = require('./test-helper') + +var net = require('net') + +var Server = function(response) { + this.server = undefined + this.socket = undefined + this.response = response +} + +Server.prototype.start = function (cb) { + // this is our fake postgres server + // it responds with our specified response immediatley after receiving every buffer + // this is sufficient into convincing the client its connectet to a valid backend + // if we respond with a readyForQuery message + this.server = net.createServer(function (socket) { + this.socket = socket + if (this.response) { + this.socket.on('data', function (data) { + // deny request for SSL + if (data.length == 8) { + this.socket.write(new Buffer('N', 'utf8')) + // consider all authentication requests as good + } else if (!data[0]) { + this.socket.write(buffers.authenticationOk()) + // respond with our canned response + } else { + this.socket.write(this.response) + } + }.bind(this)) + } + }.bind(this)) + + var port = 54321 + + var options = { + host: 'localhost', + port: port, + } + this.server.listen(options.port, options.host, function () { + cb(options) + }) +} + +Server.prototype.drop = function () { + this.socket.end() +} + +Server.prototype.close = function (cb) { + this.server.close(cb) +} + +var testServer = function (server, cb) { + // wait for our server to start + server.start(function(options) { + // connect a client to it + var client = new helper.Client(options) + client.connect() + + // after 50 milliseconds, drop the client + setTimeout(function() { + server.drop() + }, 50) + + // blow up if we don't receive an error + var timeoutId = setTimeout(function () { + throw new Error('Client should have emitted an error but it did not.') + }, 5000) + + // return our wait token + client.on('error', function () { + clearTimeout(timeoutId) + server.close(cb) + }) + }) +} + +// test being disconnected after readyForQuery +const respondingServer = new Server(buffers.readyForQuery()) +testServer(respondingServer, function () { + process.stdout.write('.') + // test being disconnected from a server that never responds + const silentServer = new Server() + testServer(silentServer, function () { + process.stdout.write('.') + }) +}) diff --git a/test/integration/client/query-error-handling-prepared-statement-tests.js b/test/integration/client/query-error-handling-prepared-statement-tests.js index 151646586..77615ff3a 100644 --- a/test/integration/client/query-error-handling-prepared-statement-tests.js +++ b/test/integration/client/query-error-handling-prepared-statement-tests.js @@ -29,12 +29,18 @@ test('query killed during query execution of prepared statement', function() { var client = new Client(helper.args); client.connect(assert.success(function() { var sleepQuery = 'select pg_sleep($1)'; - var query1 = client.query({ + + const queryConfig = { name: 'sleep query', text: sleepQuery, - values: [5] }, - assert.calls(function(err, result) { - assert.equal(err.message, 'terminating connection due to administrator command'); + values: [5], + }; + + // client should emit an error because it is unexpectedly disconnected + assert.emits(client, 'error') + + var query1 = client.query(queryConfig, assert.calls(function(err, result) { + assert.equal(err.message, 'terminating connection due to administrator command'); })); query1.on('error', function(err) { @@ -53,7 +59,6 @@ test('query killed during query execution of prepared statement', function() { })); }); - test('client end during query execution of prepared statement', function() { var client = new Client(helper.args); client.connect(assert.success(function() { diff --git a/test/unit/client/stream-and-query-error-interaction-tests.js b/test/unit/client/stream-and-query-error-interaction-tests.js index 02d66c628..a60e1f734 100644 --- a/test/unit/client/stream-and-query-error-interaction-tests.js +++ b/test/unit/client/stream-and-query-error-interaction-tests.js @@ -7,12 +7,14 @@ test('emits end when not in query', function() { stream.write = function() { //NOOP } + var client = new Client({connection: new Connection({stream: stream})}); client.connect(assert.calls(function() { client.query('SELECT NOW()', assert.calls(function(err, result) { assert(err); })); })); + assert.emits(client, 'error'); assert.emits(client, 'end'); client.connection.emit('connect'); process.nextTick(function() { From 58691218affa37b41529376c5167252846bb93fa Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Fri, 9 Jun 2017 12:33:55 -0500 Subject: [PATCH 17/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 590a91557..40a2c6efa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.2.3", + "version": "6.2.4", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From 5421e9dc0fffbbf257a9249dc17e965422ebbcc8 Mon Sep 17 00:00:00 2001 From: Amila Welihinda Date: Sun, 11 Jun 2017 12:59:33 -0700 Subject: [PATCH 18/40] Added MIT License --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..11cda4c0a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Brian C + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From bbb759fba41028fc2b6ddef43ffea35f4695b8ed Mon Sep 17 00:00:00 2001 From: Brian C Date: Mon, 12 Jun 2017 09:47:39 -0500 Subject: [PATCH 19/40] Create LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 11cda4c0a..a7b546d2d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Brian C +Copyright (c) 2010 - 2017 Brian Carlson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From b5b49eb895727e01290e90d08292c0d61ab86322 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sun, 18 Jun 2017 11:48:22 -0500 Subject: [PATCH 20/40] Add deprecations This adds deprecations in preparation for `pg@7.0` - deprecate using event emitters on automatically created results from client.query. - deprecate query.promise() - it should never have been a public method and it's not documented. I need to do better about using _ prefix on private methods in the future. - deprecate singleton pool on the `pg` object. `pg.connect`, `pg.end`, and `pg.cancel`. --- lib/client.js | 5 ++++- lib/index.js | 12 ++++++------ lib/native/index.js | 31 +++++++++++++++++++++++++++++++ lib/native/query.js | 32 ++++++++++++++++++++++---------- lib/query.js | 20 +++++++++++++++----- lib/utils.js | 19 ++++++++++++++++++- test/test-helper.js | 2 ++ 7 files changed, 98 insertions(+), 23 deletions(-) diff --git a/lib/client.js b/lib/client.js index 78d8bd7b9..bbda1ddf5 100644 --- a/lib/client.js +++ b/lib/client.js @@ -13,6 +13,7 @@ var pgPass = require('pgpass'); var TypeOverrides = require('./type-overrides'); var ConnectionParameters = require('./connection-parameters'); +var utils = require('./utils'); var Query = require('./query'); var defaults = require('./defaults'); var Connection = require('./connection'); @@ -348,10 +349,12 @@ Client.prototype.copyTo = function (text) { throw new Error("For PostgreSQL COPY TO/COPY FROM support npm install pg-copy-streams"); }; +var DeprecatedEmitterQuery = utils.deprecateEventEmitter(Query); + Client.prototype.query = function(config, values, callback) { //can take in strings, config object or query object var query = (typeof config.submit == 'function') ? config : - new Query(config, values, callback); + new DeprecatedEmitterQuery(config, values, callback); if(this.binary && !query.binary) { query.binary = true; } diff --git a/lib/index.js b/lib/index.js index a2c2792f4..17abb5bc8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -27,7 +27,7 @@ var PG = function(clientConstructor) { util.inherits(PG, EventEmitter); -PG.prototype.end = function() { +PG.prototype.end = util.deprecate(function() { var self = this; var keys = Object.keys(this._pools); var count = keys.length; @@ -47,9 +47,9 @@ PG.prototype.end = function() { }); }); } -}; +}, 'PG.end is deprecated - please see the upgrade guide at https://node-postgres.com/guides/upgrading'); -PG.prototype.connect = function(config, callback) { +PG.prototype.connect = util.deprecate(function(config, callback) { if(typeof config == "function") { callback = config; config = null; @@ -75,10 +75,10 @@ PG.prototype.connect = function(config, callback) { }.bind(this)); } return pool.connect(callback); -}; +}, 'PG.connect is deprecated - please see the upgrade guide at https://node-postgres.com/guides/upgrading'); // cancel the query running on the given client -PG.prototype.cancel = function(config, client, query) { +PG.prototype.cancel = util.deprecate(function(config, client, query) { if(client.native) { return client.cancel(query); } @@ -89,7 +89,7 @@ PG.prototype.cancel = function(config, client, query) { } var cancellingClient = new this.Client(c); cancellingClient.cancel(client, query); -}; +}, 'PG.cancel is deprecated - use client.cancel instead'); if(typeof process.env.NODE_PG_FORCE_NATIVE != 'undefined') { module.exports = new PG(require('./native')); diff --git a/lib/native/index.js b/lib/native/index.js index 1e192e8bf..99c029fbc 100644 --- a/lib/native/index.js +++ b/lib/native/index.js @@ -46,6 +46,8 @@ var Client = module.exports = function(config) { this.namedQueries = {}; }; +Client.Query = NativeQuery; + util.inherits(Client, EventEmitter); //connect to the backend @@ -139,6 +141,35 @@ Client.prototype.query = function(config, values, callback) { return query; }; +var DeprecatedQuery = require('../utils').deprecateEventEmitter(NativeQuery); + +//send a query to the server +//this method is highly overloaded to take +//1) string query, optional array of parameters, optional function callback +//2) object query with { +// string query +// optional array values, +// optional function callback instead of as a separate parameter +// optional string name to name & cache the query plan +// optional string rowMode = 'array' for an array of results +// } +Client.prototype.query = function(config, values, callback) { + if (typeof config.submit == 'function') { + // accept query(new Query(...), (err, res) => { }) style + if (typeof values == 'function') { + config.callback = values; + } + this._queryQueue.push(config); + this._pulseQueryQueue(); + return config; + } + + var query = new DeprecatedQuery(config, values, callback); + this._queryQueue.push(query); + this._pulseQueryQueue(); + return query; +}; + //disconnect from the backend server Client.prototype.end = function(cb) { var self = this; diff --git a/lib/native/query.js b/lib/native/query.js index fcb1eff4f..3d6b3c032 100644 --- a/lib/native/query.js +++ b/lib/native/query.js @@ -11,15 +11,15 @@ var util = require('util'); var utils = require('../utils'); var NativeResult = require('./result'); -var NativeQuery = module.exports = function(native) { +var NativeQuery = module.exports = function(config, values, callback) { EventEmitter.call(this); - this.native = native; - this.text = null; - this.values = null; - this.name = null; - this.callback = null; + config = utils.normalizeQueryConfig(config, values, callback); + this.text = config.text; + this.values = config.values; + this.name = config.name; + this.callback = config.callback; this.state = 'new'; - this._arrayMode = false; + this._arrayMode = config.rowMode == 'array'; //if the 'row' event is listened for //then emit them as they come in @@ -34,6 +34,13 @@ var NativeQuery = module.exports = function(native) { util.inherits(NativeQuery, EventEmitter); +// TODO - remove in 7.0 +// this maintains backwards compat so someone could instantiate a query +// manually: `new Query().then()`... +NativeQuery._on = NativeQuery.on; +NativeQuery._once = NativeQuery.once; + + NativeQuery.prototype.then = function(onSuccess, onFailure) { return this.promise().then(onSuccess, onFailure); }; @@ -42,15 +49,19 @@ NativeQuery.prototype.catch = function(callback) { return this.promise().catch(callback); }; -NativeQuery.prototype.promise = function() { +NativeQuery.prototype._getPromise = function() { if (this._promise) return this._promise; this._promise = new Promise(function(resolve, reject) { - this.once('end', resolve); - this.once('error', reject); + this._once('end', resolve); + this._once('error', reject); }.bind(this)); return this._promise; }; +NativeQuery.prototype.promise = util.deprecate(function() { + return this._getPromise(); +}, 'Query.promise() is deprecated - see the upgrade guide at https://node-postgres.com/guides/upgrading'); + NativeQuery.prototype.handleError = function(err) { var self = this; //copy pq error fields into the error object @@ -71,6 +82,7 @@ NativeQuery.prototype.handleError = function(err) { NativeQuery.prototype.submit = function(client) { this.state = 'running'; var self = this; + self.native = client.native; client.native.arrayMode = this._arrayMode; var after = function(err, rows) { diff --git a/lib/query.js b/lib/query.js index 4d49dafd3..652db38e7 100644 --- a/lib/query.js +++ b/lib/query.js @@ -40,23 +40,33 @@ var Query = function(config, values, callback) { util.inherits(Query, EventEmitter); +// TODO - remove in 7.0 +// this maintains backwards compat so someone could instantiate a query +// manually: `new Query().then()`... +Query._on = Query.on; +Query._once = Query.once; + Query.prototype.then = function(onSuccess, onFailure) { - return this.promise().then(onSuccess, onFailure); + return this._getPromise().then(onSuccess, onFailure); }; Query.prototype.catch = function(callback) { - return this.promise().catch(callback); + return this._getPromise().catch(callback); }; -Query.prototype.promise = function() { +Query.prototype._getPromise = function () { if (this._promise) return this._promise; this._promise = new Promise(function(resolve, reject) { - this.once('end', resolve); - this.once('error', reject); + this._once('end', resolve); + this._once('error', reject); }.bind(this)); return this._promise; }; +Query.prototype.promise = util.deprecate(function() { + return this._getPromise(); +}, 'Query.promise() is deprecated - see the upgrade guide at https://node-postgres.com/guides/upgrading'); + Query.prototype.requiresPreparation = function() { //named queries must always be prepared if(this.name) { return true; } diff --git a/lib/utils.js b/lib/utils.js index 82d1aefd5..5049b5afd 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -6,6 +6,8 @@ * README.md file in the root directory of this source tree. */ +var util = require('util'); + var defaults = require('./defaults'); function escapeElement(elementRepresentation) { @@ -137,11 +139,26 @@ function normalizeQueryConfig (config, values, callback) { return config; } +var queryEventEmitterOverloadDeprecationMessage = 'Using the automatically created return value from client.query as an event emitter is deprecated. Please see the upgrade guide at https://node-postgres.com/guides/upgrading'; + +var deprecateEventEmitter = function(Emitter) { + var Result = function () { + Emitter.apply(this, arguments); + }; + util.inherits(Result, Emitter); + Result.prototype._on = Result.prototype.on; + Result.prototype._once = Result.prototype.once; + Result.prototype.on = util.deprecate(Result.prototype.on, queryEventEmitterOverloadDeprecationMessage); + Result.prototype.once = util.deprecate(Result.prototype.once, queryEventEmitterOverloadDeprecationMessage); + return Result; +}; + module.exports = { prepareValue: function prepareValueWrapper (value) { //this ensures that extra arguments do not get passed into prepareValue //by accident, eg: from calling values.map(utils.prepareValue) return prepareValue(value); }, - normalizeQueryConfig: normalizeQueryConfig + normalizeQueryConfig: normalizeQueryConfig, + deprecateEventEmitter: deprecateEventEmitter, }; diff --git a/test/test-helper.js b/test/test-helper.js index d8e068764..3bb4b6c9f 100644 --- a/test/test-helper.js +++ b/test/test-helper.js @@ -1,6 +1,8 @@ //make assert a global... assert = require('assert'); +process.noDeprecation = true + //support for node@0.10.x if (typeof Promise == 'undefined') { global.Promise = require('promise-polyfill') From 1e04fdb7e01031f91872f5f1300b9e1d13ce7cc3 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Mon, 19 Jun 2017 16:07:12 -0500 Subject: [PATCH 21/40] Add changes for v6.3.0 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56b294133..09c1f89da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ For richer information consult the commit log on github with referenced pull req We do not include break-fix version release in this file. +### v6.3.0 + +- Deprecate `pg.connect` `pg.end` and `pg.cancel` - favor using `new pg.Pool()` instead of pg singleton. +- Deprecate undocumented but possibly used `query.promise()` method. Use the promise returned directly from `client.query` / `pool.query`. +- Deprecate returning an automatically created query result from `client.query`. Instead return more idomatic responses for callback/promise methods. + ### v6.2.0 - Add support for [parsing `replicationStart` messages](https://github.com/brianc/node-postgres/pull/1271/files). From f7a946155f78b1b05adda1c4e57857716eed82d9 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Mon, 19 Jun 2017 16:08:23 -0500 Subject: [PATCH 22/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 40a2c6efa..87d2c2fa4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.2.4", + "version": "6.3.0", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From 842803c7efd5a811a2e96b432e610b9acf98d0af Mon Sep 17 00:00:00 2001 From: Brian C Date: Tue, 20 Jun 2017 08:59:07 -0500 Subject: [PATCH 23/40] Fix over-eager deprecation warnings (#1333) * WIP * Remove console.log messages --- lib/query.js | 14 +++++++++++-- lib/utils.js | 2 +- test/integration/client/deprecation-tests.js | 22 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 test/integration/client/deprecation-tests.js diff --git a/lib/query.js b/lib/query.js index 652db38e7..b8f6d77eb 100644 --- a/lib/query.js +++ b/lib/query.js @@ -57,8 +57,18 @@ Query.prototype.catch = function(callback) { Query.prototype._getPromise = function () { if (this._promise) return this._promise; this._promise = new Promise(function(resolve, reject) { - this._once('end', resolve); - this._once('error', reject); + var onEnd = function (result) { + this.removeListener('error', onError); + this.removeListener('end', onEnd); + resolve(result); + }; + var onError = function (err) { + this.removeListener('error', onError); + this.removeListener('end', onEnd); + reject(err); + }; + this._on('end', onEnd); + this._on('error', onError); }.bind(this)); return this._promise; }; diff --git a/lib/utils.js b/lib/utils.js index 5049b5afd..eccf5069d 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -139,7 +139,7 @@ function normalizeQueryConfig (config, values, callback) { return config; } -var queryEventEmitterOverloadDeprecationMessage = 'Using the automatically created return value from client.query as an event emitter is deprecated. Please see the upgrade guide at https://node-postgres.com/guides/upgrading'; +var queryEventEmitterOverloadDeprecationMessage = 'Using the automatically created return value from client.query as an event emitter is deprecated and will be removed in pg@7.0. Please see the upgrade guide at https://node-postgres.com/guides/upgrading'; var deprecateEventEmitter = function(Emitter) { var Result = function () { diff --git a/test/integration/client/deprecation-tests.js b/test/integration/client/deprecation-tests.js new file mode 100644 index 000000000..39160d5a5 --- /dev/null +++ b/test/integration/client/deprecation-tests.js @@ -0,0 +1,22 @@ +var helper = require('./test-helper') +process.noDeprecation = false +process.on('warning', function () { + throw new Error('Should not emit deprecation warning') +}) + +var client = new helper.pg.Client() + +client.connect(function (err) { + if (err) throw err + client.query('SELECT NOW()') + .then(function (res) { + client.query('SELECT NOW()', function () { + client.end(function () { + }) + }) + }).catch(function (err) { + setImmediate(function () { + throw err + }) + }) +}) From e4469340aee6703eaa9f1879b82d7eeab99a441e Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Tue, 20 Jun 2017 09:17:27 -0500 Subject: [PATCH 24/40] Fix deprecation warnings in native driver --- lib/native/index.js | 6 ++++-- lib/native/query.js | 20 +++++++++++++++----- test/integration/client/deprecation-tests.js | 5 +++-- test/test-helper.js | 2 -- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/native/index.js b/lib/native/index.js index 99c029fbc..473500984 100644 --- a/lib/native/index.js +++ b/lib/native/index.js @@ -209,9 +209,11 @@ Client.prototype._pulseQueryQueue = function(initialConnection) { this._activeQuery = query; query.submit(this); var self = this; - query.once('_done', function() { + var pulseOnDone = function() { self._pulseQueryQueue(); - }); + query.removeListener('_done', pulseOnDone); + }; + query._on('_done', pulseOnDone); }; //attempt to cancel an in-progress query diff --git a/lib/native/query.js b/lib/native/query.js index 3d6b3c032..88e19f5f9 100644 --- a/lib/native/query.js +++ b/lib/native/query.js @@ -27,7 +27,7 @@ var NativeQuery = module.exports = function(config, values, callback) { //this has almost no meaning because libpq //reads all rows into memory befor returning any this._emitRowEvents = false; - this.on('newListener', function(event) { + this._on('newListener', function(event) { if(event === 'row') this._emitRowEvents = true; }.bind(this)); }; @@ -42,18 +42,28 @@ NativeQuery._once = NativeQuery.once; NativeQuery.prototype.then = function(onSuccess, onFailure) { - return this.promise().then(onSuccess, onFailure); + return this._getPromise().then(onSuccess, onFailure); }; NativeQuery.prototype.catch = function(callback) { - return this.promise().catch(callback); + return this._getPromise().catch(callback); }; NativeQuery.prototype._getPromise = function() { if (this._promise) return this._promise; this._promise = new Promise(function(resolve, reject) { - this._once('end', resolve); - this._once('error', reject); + var onEnd = function (result) { + this.removeListener('error', onError); + this.removeListener('end', onEnd); + resolve(result); + }; + var onError = function (err) { + this.removeListener('error', onError); + this.removeListener('end', onEnd); + reject(err); + }; + this._on('end', onEnd); + this._on('error', onError); }.bind(this)); return this._promise; }; diff --git a/test/integration/client/deprecation-tests.js b/test/integration/client/deprecation-tests.js index 39160d5a5..21decc46f 100644 --- a/test/integration/client/deprecation-tests.js +++ b/test/integration/client/deprecation-tests.js @@ -1,6 +1,7 @@ var helper = require('./test-helper') -process.noDeprecation = false -process.on('warning', function () { + +process.on('warning', function (warning) { + console.log(warning) throw new Error('Should not emit deprecation warning') }) diff --git a/test/test-helper.js b/test/test-helper.js index 3bb4b6c9f..d8e068764 100644 --- a/test/test-helper.js +++ b/test/test-helper.js @@ -1,8 +1,6 @@ //make assert a global... assert = require('assert'); -process.noDeprecation = true - //support for node@0.10.x if (typeof Promise == 'undefined') { global.Promise = require('promise-polyfill') From afe249896fb2b98373004c433b6d18b74ba7946e Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Tue, 20 Jun 2017 09:30:59 -0500 Subject: [PATCH 25/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 87d2c2fa4..635b49fb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.3.0", + "version": "6.3.1", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From 860cccd53105f7bc32fed8b1de69805f0ecd12eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eder=20=C3=81vila=20Prado?= Date: Tue, 20 Jun 2017 17:22:29 -0300 Subject: [PATCH 26/40] fix for server enconding when using SQL_ASCII and latin1 enconding --- lib/client.js | 5 +++-- lib/connection.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/client.js b/lib/client.js index bbda1ddf5..a031eb3e3 100644 --- a/lib/client.js +++ b/lib/client.js @@ -39,11 +39,12 @@ var Client = function(config) { this.connection = c.connection || new Connection({ stream: c.stream, ssl: this.connectionParameters.ssl, - keepAlive: c.keepAlive || false + keepAlive: c.keepAlive || false, + client_encoding: this.connectionParameters.client_encoding || 'utf8', }); this.queryQueue = []; this.binary = c.binary || defaults.binary; - this.encoding = 'utf8'; + this.encoding = this.connectionParameters.client_encoding || 'utf8'; this.processID = null; this.secretKey = null; this.ssl = this.connectionParameters.ssl || false; diff --git a/lib/connection.js b/lib/connection.js index b031ece16..0fb874cf5 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -39,7 +39,7 @@ var Connection = function(config) { this.lastOffset = 0; this.buffer = null; this.offset = null; - this.encoding = 'utf8'; + this.encoding = config.client_encoding || 'utf8'; this.parsedStatements = {}; this.writer = new Writer(); this.ssl = config.ssl || false; From 055e708dc614b1d54605cecfa73aa75e18195f73 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Wed, 21 Jun 2017 12:29:57 -0500 Subject: [PATCH 27/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 635b49fb9..3659c7616 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.3.1", + "version": "6.3.2", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From 7636f3630bb5d59f65ee7b89726dff63cd4768e2 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Wed, 21 Jun 2017 12:33:33 -0500 Subject: [PATCH 28/40] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09c1f89da..e955bb523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ For richer information consult the commit log on github with referenced pull req We do not include break-fix version release in this file. +### v6.4.0 + +- Add support for passing `client_encoding` as a connection parameter. Used when decoding strings in the JavaScript driver. The default is still `utf8`. + ### v6.3.0 - Deprecate `pg.connect` `pg.end` and `pg.cancel` - favor using `new pg.Pool()` instead of pg singleton. From a0a050702e2aa6cbda2bf7068f765ea9a0943588 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Wed, 21 Jun 2017 12:33:43 -0500 Subject: [PATCH 29/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3659c7616..f48a9bf40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.3.2", + "version": "6.4.0", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From dbf3bd3304a41d759f5c5c0bf86a2f4b6ac8d7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20B=C3=B6hlmark?= Date: Mon, 26 Jun 2017 12:52:09 +0200 Subject: [PATCH 30/40] use consistent syntax for semver ranges --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f48a9bf40..bb6aac1a2 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "pg-connection-string": "0.1.3", "pg-pool": "1.*", "pg-types": "1.*", - "pgpass": "1.x", + "pgpass": "1.*", "semver": "4.3.2" }, "devDependencies": { From c2af53a24e6a6bf61fe0f40e5448540dbe9e2d56 Mon Sep 17 00:00:00 2001 From: 2Pacalypse- Date: Sat, 27 May 2017 21:06:37 +0200 Subject: [PATCH 31/40] Properly insert buffers in arrays. Before this commit, when someone tried to insert a Buffer into an array, the library would try to escape it (by calling the `escapeElement` on it), which would fail because buffers don't have a `replace` method. --- lib/utils.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/utils.js b/lib/utils.js index eccf5069d..56a45cb06 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -33,6 +33,9 @@ function arrayString(val) { else if(Array.isArray(val[i])) { result = result + arrayString(val[i]); } + else if(val[i] instanceof Buffer) { + result += '\\\\x' + val[i].toString('hex'); + } else { result += escapeElement(prepareValue(val[i])); From e44d83f02ffe1ec5b7907d3e62268af01f701291 Mon Sep 17 00:00:00 2001 From: 2Pacalypse- Date: Sat, 10 Jun 2017 17:08:10 +0200 Subject: [PATCH 32/40] Add the test for arrays of buffers. --- test/unit/utils-tests.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/unit/utils-tests.js b/test/unit/utils-tests.js index d640f9880..685b59e38 100644 --- a/test/unit/utils-tests.js +++ b/test/unit/utils-tests.js @@ -126,6 +126,13 @@ test('prepareValue: date array prepared properly', function() { helper.resetTimezoneOffset(); }); +test('prepareValue: buffer array prepared properly', function() { + var buffer1 = Buffer.from('dead', 'hex'); + var buffer2 = Buffer.from('beef', 'hex'); + var out = utils.prepareValue([buffer1, buffer2]); + assert.strictEqual(out, '{\\\\xdead,\\\\xbeef}'); +}); + test('prepareValue: arbitrary objects prepared properly', function() { var out = utils.prepareValue({ x: 42 }); assert.strictEqual(out, '{"x":42}'); From e52512cedbd72a0be48c2a02ea30753fb964c854 Mon Sep 17 00:00:00 2001 From: 2Pacalypse- Date: Mon, 12 Jun 2017 22:12:06 +0200 Subject: [PATCH 33/40] Adjust the test for arrays of buffers to work across all node versions. --- test/unit/utils-tests.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/utils-tests.js b/test/unit/utils-tests.js index 685b59e38..19c0b8db7 100644 --- a/test/unit/utils-tests.js +++ b/test/unit/utils-tests.js @@ -127,8 +127,8 @@ test('prepareValue: date array prepared properly', function() { }); test('prepareValue: buffer array prepared properly', function() { - var buffer1 = Buffer.from('dead', 'hex'); - var buffer2 = Buffer.from('beef', 'hex'); + var buffer1 = Buffer.from ? Buffer.from('dead', 'hex') : new Buffer('dead', 'hex'); + var buffer2 = Buffer.from ? Buffer.from('beef', 'hex') : new Buffer('beef', 'hex'); var out = utils.prepareValue([buffer1, buffer2]); assert.strictEqual(out, '{\\\\xdead,\\\\xbeef}'); }); From bc0b03e0b058a211bedd5c78b87491e153640a5b Mon Sep 17 00:00:00 2001 From: Brian Carlson Date: Thu, 13 Jul 2017 22:29:42 -0500 Subject: [PATCH 34/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bb6aac1a2..7bd05f7f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.4.0", + "version": "6.4.1", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From 9bcf55dda97e122e7eee19b5ef68d2c127ac5ab7 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 12 Aug 2017 16:04:29 -0500 Subject: [PATCH 35/40] Fix vulnerability --- lib/result.js | 15 ++++++++------- package.json | 1 + .../integration/client/field-name-escape-tests.js | 10 ++++++++++ 3 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 test/integration/client/field-name-escape-tests.js diff --git a/lib/result.js b/lib/result.js index 093a73c37..fbf98cdf9 100644 --- a/lib/result.js +++ b/lib/result.js @@ -7,6 +7,7 @@ */ var types = require('pg-types'); +var escape = require('js-string-escape'); //result object returned from query //in the 'end' event and also @@ -75,13 +76,13 @@ Result.prototype.addRow = function(row) { var inlineParser = function(fieldName, i) { return "\nthis['" + - //fields containing single quotes will break - //the evaluated javascript unless they are escaped - //see https://github.com/brianc/node-postgres/issues/507 - //Addendum: However, we need to make sure to replace all - //occurences of apostrophes, not just the first one. - //See https://github.com/brianc/node-postgres/issues/934 - fieldName.replace(/'/g, "\\'") + + // fields containing single quotes will break + // the evaluated javascript unless they are escaped + // see https://github.com/brianc/node-postgres/issues/507 + // Addendum: However, we need to make sure to replace all + // occurences of apostrophes, not just the first one. + // See https://github.com/brianc/node-postgres/issues/934 + escape(fieldName) + "'] = " + "rowData[" + i + "] == null ? null : parsers[" + i + "](rowData[" + i + "]);"; }; diff --git a/package.json b/package.json index 7bd05f7f4..2bab68be2 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "dependencies": { "buffer-writer": "1.0.1", "packet-reader": "0.3.1", + "js-string-escape": "1.0.1", "pg-connection-string": "0.1.3", "pg-pool": "1.*", "pg-types": "1.*", diff --git a/test/integration/client/field-name-escape-tests.js b/test/integration/client/field-name-escape-tests.js new file mode 100644 index 000000000..146ad1b68 --- /dev/null +++ b/test/integration/client/field-name-escape-tests.js @@ -0,0 +1,10 @@ +var pg = require('./test-helper').pg + +var sql = 'SELECT 1 AS "\\\'/*", 2 AS "\\\'*/\n + process.exit(-1)] = null;\n//"' + +var client = new pg.Client() +client.connect() +client.query(sql, function (err, res) { + if (err) throw err + client.end() +}) From d4aa616b21b2d1df2eb9ab5db459388468ac56b2 Mon Sep 17 00:00:00 2001 From: "Brian M. Carlson" Date: Sat, 12 Aug 2017 16:32:30 -0500 Subject: [PATCH 36/40] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2bab68be2..e09245efc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pg", - "version": "6.4.1", + "version": "6.4.2", "description": "PostgreSQL client - pure javascript & libpq with the same API", "keywords": [ "postgres", From 8ce7e13b93cb9a071d0474035a010fe89cd567ee Mon Sep 17 00:00:00 2001 From: Charmander <~@charmander.me> Date: Tue, 14 Nov 2017 20:50:47 -0800 Subject: [PATCH 37/40] Make tests compatible with PostgreSQL 10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PostgreSQL 10 reports its version as only `major.minor`, so it can’t be parsed with semver. The `server_version_num` setting is a major version followed by a four-digit minor version since version 10, and was a three-digit major version followed by a two-digit minor version before that. --- .../integration/client/json-type-parsing-tests.js | 2 +- ...ery-error-handling-prepared-statement-tests.js | 2 +- .../client/query-error-handling-tests.js | 6 +++--- test/integration/client/result-metadata-tests.js | 2 +- test/integration/connection-pool/error-tests.js | 2 +- test/integration/test-helper.js | 15 +++++++-------- 6 files changed, 14 insertions(+), 15 deletions(-) diff --git a/test/integration/client/json-type-parsing-tests.js b/test/integration/client/json-type-parsing-tests.js index 1c0759bf3..d216aef48 100644 --- a/test/integration/client/json-type-parsing-tests.js +++ b/test/integration/client/json-type-parsing-tests.js @@ -9,7 +9,7 @@ if (helper.config.binary) { test('can read and write json', function() { helper.pg.connect(helper.config, function(err, client, done) { assert.ifError(err); - helper.versionGTE(client, '9.2.0', assert.success(function(jsonSupported) { + helper.versionGTE(client, '90200', assert.success(function(jsonSupported) { if(!jsonSupported) { console.log('skip json test on older versions of postgres'); done(); diff --git a/test/integration/client/query-error-handling-prepared-statement-tests.js b/test/integration/client/query-error-handling-prepared-statement-tests.js index 77615ff3a..c9bb260db 100644 --- a/test/integration/client/query-error-handling-prepared-statement-tests.js +++ b/test/integration/client/query-error-handling-prepared-statement-tests.js @@ -6,7 +6,7 @@ function killIdleQuery(targetQuery) { var pidColName = 'procpid' var queryColName = 'current_query'; client2.connect(assert.success(function() { - helper.versionGTE(client2, '9.2.0', assert.success(function(isGreater) { + helper.versionGTE(client2, '90200', assert.success(function(isGreater) { if(isGreater) { pidColName = 'pid'; queryColName = 'query'; diff --git a/test/integration/client/query-error-handling-tests.js b/test/integration/client/query-error-handling-tests.js index 2618a49df..e7bd56a58 100644 --- a/test/integration/client/query-error-handling-tests.js +++ b/test/integration/client/query-error-handling-tests.js @@ -7,7 +7,7 @@ test('error during query execution', function() { var sleepQuery = 'select pg_sleep(5)'; var pidColName = 'procpid' var queryColName = 'current_query'; - helper.versionGTE(client, '9.2.0', assert.success(function(isGreater) { + helper.versionGTE(client, 90200, assert.success(function(isGreater) { if(isGreater) { pidColName = 'pid'; queryColName = 'query'; @@ -43,7 +43,7 @@ if(helper.config.native) return; test('9.3 column error fields', function() { var client = new Client(helper.args); client.connect(assert.success(function() { - helper.versionGTE(client, '9.3.0', assert.success(function(isGreater) { + helper.versionGTE(client, 90300, assert.success(function(isGreater) { if(!isGreater) { return client.end(); } @@ -65,7 +65,7 @@ test('9.3 column error fields', function() { test('9.3 constraint error fields', function() { var client = new Client(helper.args); client.connect(assert.success(function() { - helper.versionGTE(client, '9.3.0', assert.success(function(isGreater) { + helper.versionGTE(client, 90300, assert.success(function(isGreater) { if(!isGreater) { console.log('skip 9.3 error field on older versions of postgres'); return client.end(); diff --git a/test/integration/client/result-metadata-tests.js b/test/integration/client/result-metadata-tests.js index 013630033..b9d09b5d3 100644 --- a/test/integration/client/result-metadata-tests.js +++ b/test/integration/client/result-metadata-tests.js @@ -5,7 +5,7 @@ test('should return insert metadata', function() { pg.connect(helper.config, assert.calls(function(err, client, done) { assert.isNull(err); - helper.versionGTE(client, '9.0.0', assert.success(function(hasRowCount) { + helper.versionGTE(client, '90000', assert.success(function(hasRowCount) { client.query("CREATE TEMP TABLE zugzug(name varchar(10))", assert.calls(function(err, result) { assert.isNull(err); assert.equal(result.oid, null); diff --git a/test/integration/connection-pool/error-tests.js b/test/integration/connection-pool/error-tests.js index 59121a86e..27525bf08 100644 --- a/test/integration/connection-pool/error-tests.js +++ b/test/integration/connection-pool/error-tests.js @@ -11,7 +11,7 @@ pg.connect(helper.config, assert.success(function(client, done) { pg.connect(helper.config, assert.success(function(client2, done2) { client2.id = 2; var pidColName = 'procpid'; - helper.versionGTE(client2, '9.2.0', assert.success(function(isGreater) { + helper.versionGTE(client2, '90200', assert.success(function(isGreater) { var killIdleQuery = 'SELECT pid, (SELECT pg_terminate_backend(pid)) AS killed FROM pg_stat_activity WHERE state = $1'; var params = ['idle']; if(!isGreater) { diff --git a/test/integration/test-helper.js b/test/integration/test-helper.js index c6a4922dc..155c36077 100644 --- a/test/integration/test-helper.js +++ b/test/integration/test-helper.js @@ -13,14 +13,13 @@ helper.client = function(cb) { return client; }; -var semver = require('semver'); -helper.versionGTE = function(client, versionString, callback) { - client.query('SELECT version()', assert.calls(function(err, result) { - if(err) return callback(err); - var version = result.rows[0].version.split(' ')[1]; - return callback(null, semver.gte(version, versionString)); - })); -}; +helper.versionGTE = function (client, testVersion, callback) { + client.query('SHOW server_version_num', assert.calls(function (err, result) { + if (err) return callback(err) + var version = parseInt(result.rows[0].server_version_num, 10) + return callback(null, version >= testVersion) + })) +} //export parent helper stuffs module.exports = helper; From b5be2ee38cd21762c6473ceb3fb9e7110dd287f1 Mon Sep 17 00:00:00 2001 From: Raul Marin Date: Wed, 23 May 2018 16:13:14 +0200 Subject: [PATCH 38/40] Remove deprecation warnings --- lib/index.js | 12 ++++++------ lib/native/query.js | 4 ++-- lib/query.js | 4 ++-- lib/utils.js | 5 ++--- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/index.js b/lib/index.js index 17abb5bc8..a2c2792f4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -27,7 +27,7 @@ var PG = function(clientConstructor) { util.inherits(PG, EventEmitter); -PG.prototype.end = util.deprecate(function() { +PG.prototype.end = function() { var self = this; var keys = Object.keys(this._pools); var count = keys.length; @@ -47,9 +47,9 @@ PG.prototype.end = util.deprecate(function() { }); }); } -}, 'PG.end is deprecated - please see the upgrade guide at https://node-postgres.com/guides/upgrading'); +}; -PG.prototype.connect = util.deprecate(function(config, callback) { +PG.prototype.connect = function(config, callback) { if(typeof config == "function") { callback = config; config = null; @@ -75,10 +75,10 @@ PG.prototype.connect = util.deprecate(function(config, callback) { }.bind(this)); } return pool.connect(callback); -}, 'PG.connect is deprecated - please see the upgrade guide at https://node-postgres.com/guides/upgrading'); +}; // cancel the query running on the given client -PG.prototype.cancel = util.deprecate(function(config, client, query) { +PG.prototype.cancel = function(config, client, query) { if(client.native) { return client.cancel(query); } @@ -89,7 +89,7 @@ PG.prototype.cancel = util.deprecate(function(config, client, query) { } var cancellingClient = new this.Client(c); cancellingClient.cancel(client, query); -}, 'PG.cancel is deprecated - use client.cancel instead'); +}; if(typeof process.env.NODE_PG_FORCE_NATIVE != 'undefined') { module.exports = new PG(require('./native')); diff --git a/lib/native/query.js b/lib/native/query.js index 88e19f5f9..214488f60 100644 --- a/lib/native/query.js +++ b/lib/native/query.js @@ -68,9 +68,9 @@ NativeQuery.prototype._getPromise = function() { return this._promise; }; -NativeQuery.prototype.promise = util.deprecate(function() { +NativeQuery.prototype.promise = function() { return this._getPromise(); -}, 'Query.promise() is deprecated - see the upgrade guide at https://node-postgres.com/guides/upgrading'); +}; NativeQuery.prototype.handleError = function(err) { var self = this; diff --git a/lib/query.js b/lib/query.js index b8f6d77eb..e9c3d1560 100644 --- a/lib/query.js +++ b/lib/query.js @@ -73,9 +73,9 @@ Query.prototype._getPromise = function () { return this._promise; }; -Query.prototype.promise = util.deprecate(function() { +Query.prototype.promise = function() { return this._getPromise(); -}, 'Query.promise() is deprecated - see the upgrade guide at https://node-postgres.com/guides/upgrading'); +}; Query.prototype.requiresPreparation = function() { //named queries must always be prepared diff --git a/lib/utils.js b/lib/utils.js index 56a45cb06..cd3ee2ef8 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -142,7 +142,6 @@ function normalizeQueryConfig (config, values, callback) { return config; } -var queryEventEmitterOverloadDeprecationMessage = 'Using the automatically created return value from client.query as an event emitter is deprecated and will be removed in pg@7.0. Please see the upgrade guide at https://node-postgres.com/guides/upgrading'; var deprecateEventEmitter = function(Emitter) { var Result = function () { @@ -151,8 +150,8 @@ var deprecateEventEmitter = function(Emitter) { util.inherits(Result, Emitter); Result.prototype._on = Result.prototype.on; Result.prototype._once = Result.prototype.once; - Result.prototype.on = util.deprecate(Result.prototype.on, queryEventEmitterOverloadDeprecationMessage); - Result.prototype.once = util.deprecate(Result.prototype.once, queryEventEmitterOverloadDeprecationMessage); + Result.prototype.on = Result.prototype.on; + Result.prototype.once = Result.prototype.once; return Result; }; From bc0fe8794280fee28ac6de04292cacdd893ec5a5 Mon Sep 17 00:00:00 2001 From: Raul Marin Date: Wed, 23 May 2018 18:06:52 +0200 Subject: [PATCH 39/40] Adapt travis matrix --- .travis.yml | 54 ++++++++++++++++++----------------------------------- 1 file changed, 18 insertions(+), 36 deletions(-) diff --git a/.travis.yml b/.travis.yml index 74f1e4c0c..c115a5754 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,43 +2,25 @@ language: node_js sudo: false dist: trusty before_script: - - node script/create-test-tables.js pg://postgres@127.0.0.1:5432/postgres + - node script/create-test-tables.js pg://postgres@127.0.0.1:$PGPORT/postgres env: - CC=clang CXX=clang++ npm_config_clang=1 PGUSER=postgres PGDATABASE=postgres -node_js: "6" -addons: - postgresql: "9.6" +env: + - POSTGRESQL_VERSION="9.5" + - POSTGRESQL_VERSION="10" + +node_js: + - "6" + - "8" + + +before_install: + - sudo service postgresql stop; + - sudo apt-get install -y --allow-unauthenticated --no-install-recommends --no-install-suggests postgresql-$POSTGRESQL_VERSION postgresql-client-$POSTGRESQL_VERSION postgresql-server-dev-$POSTGRESQL_VERSION + - echo -e "# TYPE DATABASE USER ADDRESS METHOD \nlocal all postgres trust\nlocal all all trust\nhost all all 127.0.0.1/32 trust" | sudo tee /etc/postgresql/$POSTGRESQL_VERSION/main/pg_hba.conf + - export PGPORT=`grep ^port /etc/postgresql/$POSTGRESQL_VERSION/main/postgresql.conf | awk '{print $3}'` + - export PGUSER=postgres + - export PGDATABASE=postgres + - sudo service postgresql restart $POSTGRESQL_VERSION; -matrix: - include: - - node_js: "0.10" - addons: - postgresql: "9.6" - env: [] - - node_js: "0.12" - addons: - postgresql: "9.6" - env: [] - - node_js: "4" - addons: - postgresql: "9.6" - - node_js: "5" - addons: - postgresql: "9.6" - - node_js: "6" - addons: - postgresql: "9.1" - dist: precise - - node_js: "6" - addons: - postgresql: "9.2" - - node_js: "6" - addons: - postgresql: "9.3" - - node_js: "6" - addons: - postgresql: "9.4" - - node_js: "6" - addons: - postgresql: "9.5" From 5e7005107d362855f5b51e4227a41a97e99c0cfb Mon Sep 17 00:00:00 2001 From: Raul Marin Date: Wed, 23 May 2018 19:34:07 +0200 Subject: [PATCH 40/40] Do no test native module --- Makefile | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4eddd4f10..a6f7b7968 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ test: test-unit test-all: jshint test-missing-native test-unit test-integration test-native test-binary +test-all-nonative: jshint test-unit test-integration test-binary update-npm: @npm i npm --global diff --git a/package.json b/package.json index 9ee1b5daf..750e8bac5 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "minNativeVersion": "1.7.0", "scripts": { "changelog": "npm i github-changes && ./node_modules/.bin/github-changes -o brianc -r node-postgres -d pulls -a -v", - "test": "make test-all connectionString=postgres://postgres@localhost:5432/postgres" + "test": "make test-all-nonative connectionString=postgres://postgres@localhost:$PGPORT/postgres" }, "license": "MIT", "engines": {