Skip to content

Commit 4fab71e

Browse files
authored
Merge branch 'brianc:master' into master
2 parents b31db25 + 947ccee commit 4fab71e

17 files changed

+215
-50
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ For richer information consult the commit log on github with referenced pull req
44

55
We do not include break-fix version release in this file.
66

7+
8+
9+
- Add optional config to [pool](https://github.com/brianc/node-postgres/pull/2568) to allow process to exit if pool is idle.
10+
11+
12+
13+
- Convert to [es6 class](https://github.com/brianc/node-postgres/pull/2553)
14+
- Add support for promises [to cursor methods](https://github.com/brianc/node-postgres/pull/2554)
15+
716
817

918
- Better [SASL](https://github.com/brianc/node-postgres/pull/2436) error messages & more validation on bad configuration.

packages/pg-cursor/index.js

+35-20
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class Cursor extends EventEmitter {
1717
this._queue = []
1818
this.state = 'initialized'
1919
this._result = new Result(this._conf.rowMode, this._conf.types)
20+
this._Promise = this._conf.Promise || global.Promise
2021
this._cb = null
2122
this._rows = null
2223
this._portal = null
@@ -198,38 +199,52 @@ class Cursor extends EventEmitter {
198199
}
199200

200201
close(cb) {
202+
let promise
203+
204+
if (!cb) {
205+
promise = new this._Promise((resolve, reject) => {
206+
cb = (err) => (err ? reject(err) : resolve())
207+
})
208+
}
209+
201210
if (!this.connection || this.state === 'done') {
202-
if (cb) {
203-
return setImmediate(cb)
204-
} else {
205-
return
206-
}
211+
setImmediate(cb)
212+
return promise
207213
}
208214

209215
this._closePortal()
210216
this.state = 'done'
211-
if (cb) {
212-
this.connection.once('readyForQuery', function () {
213-
cb()
214-
})
215-
}
217+
this.connection.once('readyForQuery', function () {
218+
cb()
219+
})
220+
221+
// Return the promise (or undefined)
222+
return promise
216223
}
217224

218225
read(rows, cb) {
219-
if (this.state === 'idle' || this.state === 'submitted') {
220-
return this._getRows(rows, cb)
221-
}
222-
if (this.state === 'busy' || this.state === 'initialized') {
223-
return this._queue.push([rows, cb])
224-
}
225-
if (this.state === 'error') {
226-
return setImmediate(() => cb(this._error))
226+
let promise
227+
228+
if (!cb) {
229+
promise = new this._Promise((resolve, reject) => {
230+
cb = (err, rows) => (err ? reject(err) : resolve(rows))
231+
})
227232
}
228-
if (this.state === 'done') {
229-
return setImmediate(() => cb(null, []))
233+
234+
if (this.state === 'idle' || this.state === 'submitted') {
235+
this._getRows(rows, cb)
236+
} else if (this.state === 'busy' || this.state === 'initialized') {
237+
this._queue.push([rows, cb])
238+
} else if (this.state === 'error') {
239+
setImmediate(() => cb(this._error))
240+
} else if (this.state === 'done') {
241+
setImmediate(() => cb(null, []))
230242
} else {
231243
throw new Error('Unknown state: ' + this.state)
232244
}
245+
246+
// Return the promise (or undefined)
247+
return promise
233248
}
234249
}
235250

packages/pg-cursor/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pg-cursor",
3-
"version": "2.6.0",
3+
"version": "2.7.1",
44
"description": "Query cursor extension for node-postgres",
55
"main": "index.js",
66
"directories": {
@@ -18,7 +18,7 @@
1818
"license": "MIT",
1919
"devDependencies": {
2020
"mocha": "^7.1.2",
21-
"pg": "^8.6.0"
21+
"pg": "^8.7.1"
2222
},
2323
"peerDependencies": {
2424
"pg": "^8"

packages/pg-cursor/test/close.js

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ describe('close', function () {
2323
})
2424
})
2525

26+
it('can close a finished cursor a promise', function (done) {
27+
const cursor = new Cursor(text)
28+
this.client.query(cursor)
29+
cursor.read(100, (err) => {
30+
assert.ifError(err)
31+
cursor.close().then(() => {
32+
this.client.query('SELECT NOW()', done)
33+
})
34+
})
35+
})
36+
2637
it('closes cursor early', function (done) {
2738
const cursor = new Cursor(text)
2839
this.client.query(cursor)

packages/pg-cursor/test/promises.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const assert = require('assert')
2+
const Cursor = require('../')
3+
const pg = require('pg')
4+
5+
const text = 'SELECT generate_series as num FROM generate_series(0, 5)'
6+
7+
describe('cursor using promises', function () {
8+
beforeEach(function (done) {
9+
const client = (this.client = new pg.Client())
10+
client.connect(done)
11+
12+
this.pgCursor = function (text, values) {
13+
return client.query(new Cursor(text, values || []))
14+
}
15+
})
16+
17+
afterEach(function () {
18+
this.client.end()
19+
})
20+
21+
it('resolve with result', async function () {
22+
const cursor = this.pgCursor(text)
23+
const res = await cursor.read(6)
24+
assert.strictEqual(res.length, 6)
25+
})
26+
27+
it('reject with error', function (done) {
28+
const cursor = this.pgCursor('select asdfasdf')
29+
cursor.read(1).catch((err) => {
30+
assert(err)
31+
done()
32+
})
33+
})
34+
35+
it('read multiple times', async function () {
36+
const cursor = this.pgCursor(text)
37+
let res
38+
39+
res = await cursor.read(2)
40+
assert.strictEqual(res.length, 2)
41+
42+
res = await cursor.read(3)
43+
assert.strictEqual(res.length, 3)
44+
45+
res = await cursor.read(1)
46+
assert.strictEqual(res.length, 1)
47+
48+
res = await cursor.read(1)
49+
assert.strictEqual(res.length, 0)
50+
})
51+
})

packages/pg-pool/index.js

+11
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class Pool extends EventEmitter {
8383

8484
this.options.max = this.options.max || this.options.poolSize || 10
8585
this.options.maxUses = this.options.maxUses || Infinity
86+
this.options.allowExitOnIdle = this.options.allowExitOnIdle || false
8687
this.log = this.options.log || function () {}
8788
this.Client = this.options.Client || Client || require('pg').Client
8889
this.Promise = this.options.Promise || global.Promise
@@ -136,6 +137,7 @@ class Pool extends EventEmitter {
136137
const idleItem = this._idle.pop()
137138
clearTimeout(idleItem.timeoutId)
138139
const client = idleItem.client
140+
client.ref && client.ref()
139141
const idleListener = idleItem.idleListener
140142

141143
return this._acquireClient(client, pendingItem, idleListener, false)
@@ -323,6 +325,15 @@ class Pool extends EventEmitter {
323325
this.log('remove idle client')
324326
this._remove(client)
325327
}, this.options.idleTimeoutMillis)
328+
329+
if (this.options.allowExitOnIdle) {
330+
// allow Node to exit if this is all that's left
331+
tid.unref()
332+
}
333+
}
334+
335+
if (this.options.allowExitOnIdle) {
336+
client.unref()
326337
}
327338

328339
this._idle.push(new IdleItem(client, idleListener, tid))

packages/pg-pool/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pg-pool",
3-
"version": "3.3.0",
3+
"version": "3.4.1",
44
"description": "Connection pool for node-postgres",
55
"main": "index.js",
66
"directories": {
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This test is meant to be spawned from idle-timeout.js
2+
if (module === require.main) {
3+
const allowExitOnIdle = process.env.ALLOW_EXIT_ON_IDLE === '1'
4+
const Pool = require('../index')
5+
6+
const pool = new Pool({ idleTimeoutMillis: 200, ...(allowExitOnIdle ? { allowExitOnIdle: true } : {}) })
7+
pool.query('SELECT NOW()', (err, res) => console.log('completed first'))
8+
pool.on('remove', () => {
9+
console.log('removed')
10+
done()
11+
})
12+
13+
setTimeout(() => {
14+
pool.query('SELECT * from generate_series(0, 1000)', (err, res) => console.log('completed second'))
15+
}, 50)
16+
}

packages/pg-pool/test/idle-timeout.js

+31
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const expect = require('expect.js')
44

55
const describe = require('mocha').describe
66
const it = require('mocha').it
7+
const { fork } = require('child_process')
8+
const path = require('path')
79

810
const Pool = require('../')
911

@@ -84,4 +86,33 @@ describe('idle timeout', () => {
8486
return pool.end()
8587
})
8688
)
89+
90+
it('unrefs the connections and timeouts so the program can exit when idle when the allowExitOnIdle option is set', function (done) {
91+
const child = fork(path.join(__dirname, 'idle-timeout-exit.js'), [], {
92+
silent: true,
93+
env: { ...process.env, ALLOW_EXIT_ON_IDLE: '1' },
94+
})
95+
let result = ''
96+
child.stdout.setEncoding('utf8')
97+
child.stdout.on('data', (chunk) => (result += chunk))
98+
child.on('error', (err) => done(err))
99+
child.on('close', () => {
100+
expect(result).to.equal('completed first\ncompleted second\n')
101+
done()
102+
})
103+
})
104+
105+
it('keeps old behavior when allowExitOnIdle option is not set', function (done) {
106+
const child = fork(path.join(__dirname, 'idle-timeout-exit.js'), [], {
107+
silent: true,
108+
})
109+
let result = ''
110+
child.stdout.setEncoding('utf8')
111+
child.stdout.on('data', (chunk) => (result += chunk))
112+
child.on('error', (err) => done(err))
113+
child.on('close', () => {
114+
expect(result).to.equal('completed first\ncompleted second\nremoved\n')
115+
done()
116+
})
117+
})
87118
})

packages/pg-query-stream/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pg-query-stream",
3-
"version": "4.1.0",
3+
"version": "4.2.1",
44
"description": "Postgres query result returned as readable stream",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",
@@ -37,13 +37,13 @@
3737
"concat-stream": "~1.0.1",
3838
"eslint-plugin-promise": "^3.5.0",
3939
"mocha": "^7.1.2",
40-
"pg": "^8.6.0",
40+
"pg": "^8.7.1",
4141
"stream-spec": "~0.3.5",
4242
"stream-tester": "0.0.5",
4343
"ts-node": "^8.5.4",
4444
"typescript": "^4.0.3"
4545
},
4646
"dependencies": {
47-
"pg-cursor": "^2.6.0"
47+
"pg-cursor": "^2.7.1"
4848
}
4949
}

packages/pg/lib/client.js

+8
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,14 @@ class Client extends EventEmitter {
577577
return result
578578
}
579579

580+
ref() {
581+
this.connection.ref()
582+
}
583+
584+
unref() {
585+
this.connection.unref()
586+
}
587+
580588
end(cb) {
581589
this._ending = true
582590

packages/pg/lib/connection.js

+8
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ class Connection extends EventEmitter {
177177
this._send(syncBuffer)
178178
}
179179

180+
ref() {
181+
this.stream.ref()
182+
}
183+
184+
unref() {
185+
this.stream.unref()
186+
}
187+
180188
end() {
181189
// 0x58 = 'X'
182190
this._ending = true

packages/pg/lib/native/client.js

+3
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,9 @@ Client.prototype.cancel = function (query) {
285285
}
286286
}
287287

288+
Client.prototype.ref = function () {}
289+
Client.prototype.unref = function () {}
290+
288291
Client.prototype.setTypeParser = function (oid, format, parseFn) {
289292
return this._types.setTypeParser(oid, format, parseFn)
290293
}

packages/pg/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pg",
3-
"version": "8.6.0",
3+
"version": "8.7.1",
44
"description": "PostgreSQL client - pure javascript & libpq with the same API",
55
"keywords": [
66
"database",
@@ -23,7 +23,7 @@
2323
"buffer-writer": "2.0.0",
2424
"packet-reader": "1.0.0",
2525
"pg-connection-string": "^2.5.0",
26-
"pg-pool": "^3.3.0",
26+
"pg-pool": "^3.4.1",
2727
"pg-protocol": "^1.5.0",
2828
"pg-types": "^2.1.0",
2929
"pgpass": "1.x"

0 commit comments

Comments
 (0)