pg 8.2.1 → 8.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/client.js +474 -464
- package/lib/connection-parameters.js +104 -107
- package/lib/connection.js +168 -173
- package/lib/defaults.js +2 -7
- package/lib/index.js +0 -7
- package/lib/native/client.js +0 -7
- package/lib/native/query.js +0 -7
- package/lib/query.js +22 -40
- package/lib/result.js +76 -81
- package/lib/type-overrides.js +0 -7
- package/lib/utils.js +0 -7
- package/package.json +4 -4
package/lib/client.js
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
/**
|
|
3
|
-
* Copyright (c) 2010-2017 Brian Carlson (brian.m.carlson@gmail.com)
|
|
4
|
-
* All rights reserved.
|
|
5
|
-
*
|
|
6
|
-
* This source code is licensed under the MIT license found in the
|
|
7
|
-
* README.md file in the root directory of this source tree.
|
|
8
|
-
*/
|
|
9
2
|
|
|
10
3
|
var EventEmitter = require('events').EventEmitter
|
|
11
4
|
var util = require('util')
|
|
@@ -19,573 +12,590 @@ var Query = require('./query')
|
|
|
19
12
|
var defaults = require('./defaults')
|
|
20
13
|
var Connection = require('./connection')
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.replication = this.connectionParameters.replication
|
|
41
|
-
|
|
42
|
-
var c = config || {}
|
|
43
|
-
|
|
44
|
-
this._Promise = c.Promise || global.Promise
|
|
45
|
-
this._types = new TypeOverrides(c.types)
|
|
46
|
-
this._ending = false
|
|
47
|
-
this._connecting = false
|
|
48
|
-
this._connected = false
|
|
49
|
-
this._connectionError = false
|
|
50
|
-
this._queryable = true
|
|
51
|
-
|
|
52
|
-
this.connection =
|
|
53
|
-
c.connection ||
|
|
54
|
-
new Connection({
|
|
55
|
-
stream: c.stream,
|
|
56
|
-
ssl: this.connectionParameters.ssl,
|
|
57
|
-
keepAlive: c.keepAlive || false,
|
|
58
|
-
keepAliveInitialDelayMillis: c.keepAliveInitialDelayMillis || 0,
|
|
59
|
-
encoding: this.connectionParameters.client_encoding || 'utf8',
|
|
15
|
+
class Client extends EventEmitter {
|
|
16
|
+
constructor(config) {
|
|
17
|
+
super()
|
|
18
|
+
|
|
19
|
+
this.connectionParameters = new ConnectionParameters(config)
|
|
20
|
+
this.user = this.connectionParameters.user
|
|
21
|
+
this.database = this.connectionParameters.database
|
|
22
|
+
this.port = this.connectionParameters.port
|
|
23
|
+
this.host = this.connectionParameters.host
|
|
24
|
+
|
|
25
|
+
// "hiding" the password so it doesn't show up in stack traces
|
|
26
|
+
// or if the client is console.logged
|
|
27
|
+
Object.defineProperty(this, 'password', {
|
|
28
|
+
configurable: true,
|
|
29
|
+
enumerable: false,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: this.connectionParameters.password,
|
|
60
32
|
})
|
|
61
|
-
this.queryQueue = []
|
|
62
|
-
this.binary = c.binary || defaults.binary
|
|
63
|
-
this.processID = null
|
|
64
|
-
this.secretKey = null
|
|
65
|
-
this.ssl = this.connectionParameters.ssl || false
|
|
66
|
-
this._connectionTimeoutMillis = c.connectionTimeoutMillis || 0
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
util.inherits(Client, EventEmitter)
|
|
70
33
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
34
|
+
this.replication = this.connectionParameters.replication
|
|
35
|
+
|
|
36
|
+
var c = config || {}
|
|
37
|
+
|
|
38
|
+
this._Promise = c.Promise || global.Promise
|
|
39
|
+
this._types = new TypeOverrides(c.types)
|
|
40
|
+
this._ending = false
|
|
41
|
+
this._connecting = false
|
|
42
|
+
this._connected = false
|
|
43
|
+
this._connectionError = false
|
|
44
|
+
this._queryable = true
|
|
45
|
+
|
|
46
|
+
this.connection =
|
|
47
|
+
c.connection ||
|
|
48
|
+
new Connection({
|
|
49
|
+
stream: c.stream,
|
|
50
|
+
ssl: this.connectionParameters.ssl,
|
|
51
|
+
keepAlive: c.keepAlive || false,
|
|
52
|
+
keepAliveInitialDelayMillis: c.keepAliveInitialDelayMillis || 0,
|
|
53
|
+
encoding: this.connectionParameters.client_encoding || 'utf8',
|
|
54
|
+
})
|
|
55
|
+
this.queryQueue = []
|
|
56
|
+
this.binary = c.binary || defaults.binary
|
|
57
|
+
this.processID = null
|
|
58
|
+
this.secretKey = null
|
|
59
|
+
this.ssl = this.connectionParameters.ssl || false
|
|
60
|
+
this._connectionTimeoutMillis = c.connectionTimeoutMillis || 0
|
|
76
61
|
}
|
|
77
62
|
|
|
78
|
-
|
|
79
|
-
enqueueError(
|
|
80
|
-
|
|
63
|
+
_errorAllQueries(err) {
|
|
64
|
+
const enqueueError = (query) => {
|
|
65
|
+
process.nextTick(() => {
|
|
66
|
+
query.handleError(err, this.connection)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this.activeQuery) {
|
|
71
|
+
enqueueError(this.activeQuery)
|
|
72
|
+
this.activeQuery = null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.queryQueue.forEach(enqueueError)
|
|
76
|
+
this.queryQueue.length = 0
|
|
81
77
|
}
|
|
82
78
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
_connect(callback) {
|
|
80
|
+
var self = this
|
|
81
|
+
var con = this.connection
|
|
82
|
+
this._connectionCallback = callback
|
|
83
|
+
|
|
84
|
+
if (this._connecting || this._connected) {
|
|
85
|
+
const err = new Error('Client has already been connected. You cannot reuse a client.')
|
|
86
|
+
process.nextTick(() => {
|
|
87
|
+
callback(err)
|
|
88
|
+
})
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
this._connecting = true
|
|
92
|
+
|
|
93
|
+
this.connectionTimeoutHandle
|
|
94
|
+
if (this._connectionTimeoutMillis > 0) {
|
|
95
|
+
this.connectionTimeoutHandle = setTimeout(() => {
|
|
96
|
+
con._ending = true
|
|
97
|
+
con.stream.destroy(new Error('timeout expired'))
|
|
98
|
+
}, this._connectionTimeoutMillis)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (this.host && this.host.indexOf('/') === 0) {
|
|
102
|
+
con.connect(this.host + '/.s.PGSQL.' + this.port)
|
|
103
|
+
} else {
|
|
104
|
+
con.connect(this.port, this.host)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// once connection is established send startup message
|
|
108
|
+
con.on('connect', function () {
|
|
109
|
+
if (self.ssl) {
|
|
110
|
+
con.requestSsl()
|
|
111
|
+
} else {
|
|
112
|
+
con.startup(self.getStartupConf())
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
con.on('sslconnect', function () {
|
|
117
|
+
con.startup(self.getStartupConf())
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
this._attachListeners(con)
|
|
86
121
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
122
|
+
con.once('end', () => {
|
|
123
|
+
const error = this._ending ? new Error('Connection terminated') : new Error('Connection terminated unexpectedly')
|
|
124
|
+
|
|
125
|
+
clearTimeout(this.connectionTimeoutHandle)
|
|
126
|
+
this._errorAllQueries(error)
|
|
127
|
+
|
|
128
|
+
if (!this._ending) {
|
|
129
|
+
// if the connection is ended without us calling .end()
|
|
130
|
+
// on this client then we have an unexpected disconnection
|
|
131
|
+
// treat this as an error unless we've already emitted an error
|
|
132
|
+
// during connection.
|
|
133
|
+
if (this._connecting && !this._connectionError) {
|
|
134
|
+
if (this._connectionCallback) {
|
|
135
|
+
this._connectionCallback(error)
|
|
136
|
+
} else {
|
|
137
|
+
this._handleErrorEvent(error)
|
|
138
|
+
}
|
|
139
|
+
} else if (!this._connectionError) {
|
|
140
|
+
this._handleErrorEvent(error)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
process.nextTick(() => {
|
|
145
|
+
this.emit('end')
|
|
146
|
+
})
|
|
94
147
|
})
|
|
95
|
-
return
|
|
96
148
|
}
|
|
97
|
-
this._connecting = true
|
|
98
149
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
150
|
+
connect(callback) {
|
|
151
|
+
if (callback) {
|
|
152
|
+
this._connect(callback)
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return new this._Promise((resolve, reject) => {
|
|
157
|
+
this._connect((error) => {
|
|
158
|
+
if (error) {
|
|
159
|
+
reject(error)
|
|
160
|
+
} else {
|
|
161
|
+
resolve()
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
})
|
|
105
165
|
}
|
|
106
166
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
167
|
+
_attachListeners(con) {
|
|
168
|
+
// password request handling
|
|
169
|
+
con.on('authenticationCleartextPassword', this._handleAuthCleartextPassword.bind(this))
|
|
170
|
+
// password request handling
|
|
171
|
+
con.on('authenticationMD5Password', this._handleAuthMD5Password.bind(this))
|
|
172
|
+
// password request handling (SASL)
|
|
173
|
+
con.on('authenticationSASL', this._handleAuthSASL.bind(this))
|
|
174
|
+
con.on('authenticationSASLContinue', this._handleAuthSASLContinue.bind(this))
|
|
175
|
+
con.on('authenticationSASLFinal', this._handleAuthSASLFinal.bind(this))
|
|
176
|
+
con.on('backendKeyData', this._handleBackendKeyData.bind(this))
|
|
177
|
+
con.on('error', this._handleErrorEvent.bind(this))
|
|
178
|
+
con.on('errorMessage', this._handleErrorMessage.bind(this))
|
|
179
|
+
con.on('readyForQuery', this._handleReadyForQuery.bind(this))
|
|
180
|
+
con.on('notice', this._handleNotice.bind(this))
|
|
181
|
+
con.on('rowDescription', this._handleRowDescription.bind(this))
|
|
182
|
+
con.on('dataRow', this._handleDataRow.bind(this))
|
|
183
|
+
con.on('portalSuspended', this._handlePortalSuspended.bind(this))
|
|
184
|
+
con.on('emptyQuery', this._handleEmptyQuery.bind(this))
|
|
185
|
+
con.on('commandComplete', this._handleCommandComplete.bind(this))
|
|
186
|
+
con.on('parseComplete', this._handleParseComplete.bind(this))
|
|
187
|
+
con.on('copyInResponse', this._handleCopyInResponse.bind(this))
|
|
188
|
+
con.on('copyData', this._handleCopyData.bind(this))
|
|
189
|
+
con.on('notification', this._handleNotification.bind(this))
|
|
111
190
|
}
|
|
112
191
|
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
function checkPgPass(cb) {
|
|
127
|
-
return function (msg) {
|
|
128
|
-
if (typeof self.password === 'function') {
|
|
129
|
-
self._Promise
|
|
130
|
-
.resolve()
|
|
131
|
-
.then(() => self.password())
|
|
132
|
-
.then((pass) => {
|
|
133
|
-
if (pass !== undefined) {
|
|
134
|
-
if (typeof pass !== 'string') {
|
|
135
|
-
con.emit('error', new TypeError('Password must be a string'))
|
|
136
|
-
return
|
|
137
|
-
}
|
|
138
|
-
self.connectionParameters.password = self.password = pass
|
|
139
|
-
} else {
|
|
140
|
-
self.connectionParameters.password = self.password = null
|
|
192
|
+
// TODO(bmc): deprecate pgpass "built in" integration since this.password can be a function
|
|
193
|
+
// it can be supplied by the user if required - this is a breaking change!
|
|
194
|
+
_checkPgPass(cb) {
|
|
195
|
+
const con = this.connection
|
|
196
|
+
if (typeof this.password === 'function') {
|
|
197
|
+
this._Promise
|
|
198
|
+
.resolve()
|
|
199
|
+
.then(() => this.password())
|
|
200
|
+
.then((pass) => {
|
|
201
|
+
if (pass !== undefined) {
|
|
202
|
+
if (typeof pass !== 'string') {
|
|
203
|
+
con.emit('error', new TypeError('Password must be a string'))
|
|
204
|
+
return
|
|
141
205
|
}
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
con.emit('error', err)
|
|
146
|
-
})
|
|
147
|
-
} else if (self.password !== null) {
|
|
148
|
-
cb(msg)
|
|
149
|
-
} else {
|
|
150
|
-
pgPass(self.connectionParameters, function (pass) {
|
|
151
|
-
if (undefined !== pass) {
|
|
152
|
-
self.connectionParameters.password = self.password = pass
|
|
206
|
+
this.connectionParameters.password = this.password = pass
|
|
207
|
+
} else {
|
|
208
|
+
this.connectionParameters.password = this.password = null
|
|
153
209
|
}
|
|
154
|
-
cb(
|
|
210
|
+
cb()
|
|
155
211
|
})
|
|
156
|
-
|
|
212
|
+
.catch((err) => {
|
|
213
|
+
con.emit('error', err)
|
|
214
|
+
})
|
|
215
|
+
} else if (this.password !== null) {
|
|
216
|
+
cb()
|
|
217
|
+
} else {
|
|
218
|
+
pgPass(this.connectionParameters, (pass) => {
|
|
219
|
+
if (undefined !== pass) {
|
|
220
|
+
this.connectionParameters.password = this.password = pass
|
|
221
|
+
}
|
|
222
|
+
cb()
|
|
223
|
+
})
|
|
157
224
|
}
|
|
158
225
|
}
|
|
159
226
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
checkPgPass(function () {
|
|
164
|
-
con.password(self.password)
|
|
227
|
+
_handleAuthCleartextPassword(msg) {
|
|
228
|
+
this._checkPgPass(() => {
|
|
229
|
+
this.connection.password(this.password)
|
|
165
230
|
})
|
|
166
|
-
|
|
231
|
+
}
|
|
167
232
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
con.password(utils.postgresMd5PasswordHash(self.user, self.password, msg.salt))
|
|
233
|
+
_handleAuthMD5Password(msg) {
|
|
234
|
+
this._checkPgPass(() => {
|
|
235
|
+
const hashedPassword = utils.postgresMd5PasswordHash(this.user, this.password, msg.salt)
|
|
236
|
+
this.connection.password(hashedPassword)
|
|
173
237
|
})
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
// password request handling (SASL)
|
|
177
|
-
var saslSession
|
|
178
|
-
con.on(
|
|
179
|
-
'authenticationSASL',
|
|
180
|
-
checkPgPass(function (msg) {
|
|
181
|
-
saslSession = sasl.startSession(msg.mechanisms)
|
|
238
|
+
}
|
|
182
239
|
|
|
183
|
-
|
|
240
|
+
_handleAuthSASL(msg) {
|
|
241
|
+
this._checkPgPass(() => {
|
|
242
|
+
this.saslSession = sasl.startSession(msg.mechanisms)
|
|
243
|
+
this.connection.sendSASLInitialResponseMessage(this.saslSession.mechanism, this.saslSession.response)
|
|
184
244
|
})
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// password request handling (SASL)
|
|
188
|
-
con.on('authenticationSASLContinue', function (msg) {
|
|
189
|
-
sasl.continueSession(saslSession, self.password, msg.data)
|
|
245
|
+
}
|
|
190
246
|
|
|
191
|
-
|
|
192
|
-
|
|
247
|
+
_handleAuthSASLContinue(msg) {
|
|
248
|
+
sasl.continueSession(this.saslSession, this.password, msg.data)
|
|
249
|
+
this.connection.sendSCRAMClientFinalMessage(this.saslSession.response)
|
|
250
|
+
}
|
|
193
251
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
252
|
+
_handleAuthSASLFinal(msg) {
|
|
253
|
+
sasl.finalizeSession(this.saslSession, msg.data)
|
|
254
|
+
this.saslSession = null
|
|
255
|
+
}
|
|
197
256
|
|
|
198
|
-
|
|
199
|
-
|
|
257
|
+
_handleBackendKeyData(msg) {
|
|
258
|
+
this.processID = msg.processID
|
|
259
|
+
this.secretKey = msg.secretKey
|
|
260
|
+
}
|
|
200
261
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
262
|
+
_handleReadyForQuery(msg) {
|
|
263
|
+
if (this._connecting) {
|
|
264
|
+
this._connecting = false
|
|
265
|
+
this._connected = true
|
|
266
|
+
clearTimeout(this.connectionTimeoutHandle)
|
|
267
|
+
|
|
268
|
+
// process possible callback argument to Client#connect
|
|
269
|
+
if (this._connectionCallback) {
|
|
270
|
+
this._connectionCallback(null, this)
|
|
271
|
+
// remove callback for proper error handling
|
|
272
|
+
// after the connect event
|
|
273
|
+
this._connectionCallback = null
|
|
274
|
+
}
|
|
275
|
+
this.emit('connect')
|
|
276
|
+
}
|
|
277
|
+
const { activeQuery } = this
|
|
278
|
+
this.activeQuery = null
|
|
279
|
+
this.readyForQuery = true
|
|
280
|
+
if (activeQuery) {
|
|
281
|
+
activeQuery.handleReadyForQuery(this.connection)
|
|
282
|
+
}
|
|
283
|
+
this._pulseQueryQueue()
|
|
284
|
+
}
|
|
205
285
|
|
|
206
|
-
|
|
286
|
+
// if we receieve an error event or error message
|
|
287
|
+
// during the connection process we handle it here
|
|
288
|
+
_handleErrorWhileConnecting(err) {
|
|
207
289
|
if (this._connectionError) {
|
|
290
|
+
// TODO(bmc): this is swallowing errors - we shouldn't do this
|
|
208
291
|
return
|
|
209
292
|
}
|
|
210
293
|
this._connectionError = true
|
|
211
|
-
clearTimeout(connectionTimeoutHandle)
|
|
212
|
-
if (
|
|
213
|
-
return
|
|
294
|
+
clearTimeout(this.connectionTimeoutHandle)
|
|
295
|
+
if (this._connectionCallback) {
|
|
296
|
+
return this._connectionCallback(err)
|
|
214
297
|
}
|
|
215
298
|
this.emit('error', err)
|
|
216
299
|
}
|
|
217
300
|
|
|
218
|
-
|
|
301
|
+
// if we're connected and we receive an error event from the connection
|
|
302
|
+
// this means the socket is dead - do a hard abort of all queries and emit
|
|
303
|
+
// the socket error on the client as well
|
|
304
|
+
_handleErrorEvent(err) {
|
|
305
|
+
if (this._connecting) {
|
|
306
|
+
return this._handleErrorWhileConnecting(err)
|
|
307
|
+
}
|
|
219
308
|
this._queryable = false
|
|
220
309
|
this._errorAllQueries(err)
|
|
221
310
|
this.emit('error', err)
|
|
222
311
|
}
|
|
223
312
|
|
|
224
|
-
|
|
313
|
+
// handle error messages from the postgres backend
|
|
314
|
+
_handleErrorMessage(msg) {
|
|
315
|
+
if (this._connecting) {
|
|
316
|
+
return this._handleErrorWhileConnecting(msg)
|
|
317
|
+
}
|
|
225
318
|
const activeQuery = this.activeQuery
|
|
226
319
|
|
|
227
320
|
if (!activeQuery) {
|
|
228
|
-
|
|
321
|
+
this._handleErrorEvent(msg)
|
|
229
322
|
return
|
|
230
323
|
}
|
|
231
324
|
|
|
232
325
|
this.activeQuery = null
|
|
233
|
-
activeQuery.handleError(msg,
|
|
326
|
+
activeQuery.handleError(msg, this.connection)
|
|
234
327
|
}
|
|
235
328
|
|
|
236
|
-
|
|
237
|
-
|
|
329
|
+
_handleRowDescription(msg) {
|
|
330
|
+
// delegate rowDescription to active query
|
|
331
|
+
this.activeQuery.handleRowDescription(msg)
|
|
332
|
+
}
|
|
238
333
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
self._connected = true
|
|
244
|
-
self._attachListeners(con)
|
|
245
|
-
con.removeListener('error', connectingErrorHandler)
|
|
246
|
-
con.removeListener('errorMessage', connectingErrorHandler)
|
|
247
|
-
con.on('error', connectedErrorHandler)
|
|
248
|
-
con.on('errorMessage', connectedErrorMessageHandler)
|
|
249
|
-
clearTimeout(connectionTimeoutHandle)
|
|
334
|
+
_handleDataRow(msg) {
|
|
335
|
+
// delegate dataRow to active query
|
|
336
|
+
this.activeQuery.handleDataRow(msg)
|
|
337
|
+
}
|
|
250
338
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
// after the connect event
|
|
256
|
-
callback = null
|
|
257
|
-
}
|
|
258
|
-
self.emit('connect')
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
con.on('readyForQuery', function () {
|
|
262
|
-
var activeQuery = self.activeQuery
|
|
263
|
-
self.activeQuery = null
|
|
264
|
-
self.readyForQuery = true
|
|
265
|
-
if (activeQuery) {
|
|
266
|
-
activeQuery.handleReadyForQuery(con)
|
|
267
|
-
}
|
|
268
|
-
self._pulseQueryQueue()
|
|
269
|
-
})
|
|
339
|
+
_handlePortalSuspended(msg) {
|
|
340
|
+
// delegate portalSuspended to active query
|
|
341
|
+
this.activeQuery.handlePortalSuspended(this.connection)
|
|
342
|
+
}
|
|
270
343
|
|
|
271
|
-
|
|
272
|
-
|
|
344
|
+
_handleEmptyQuery(msg) {
|
|
345
|
+
// delegate emptyQuery to active query
|
|
346
|
+
this.activeQuery.handleEmptyQuery(this.connection)
|
|
347
|
+
}
|
|
273
348
|
|
|
274
|
-
|
|
275
|
-
|
|
349
|
+
_handleCommandComplete(msg) {
|
|
350
|
+
// delegate commandComplete to active query
|
|
351
|
+
this.activeQuery.handleCommandComplete(msg, this.connection)
|
|
352
|
+
}
|
|
276
353
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (callback) {
|
|
284
|
-
callback(error)
|
|
285
|
-
} else {
|
|
286
|
-
connectedErrorHandler(error)
|
|
287
|
-
}
|
|
288
|
-
} else if (!this._connectionError) {
|
|
289
|
-
connectedErrorHandler(error)
|
|
290
|
-
}
|
|
354
|
+
_handleParseComplete(msg) {
|
|
355
|
+
// if a prepared statement has a name and properly parses
|
|
356
|
+
// we track that its already been executed so we don't parse
|
|
357
|
+
// it again on the same client
|
|
358
|
+
if (this.activeQuery.name) {
|
|
359
|
+
this.connection.parsedStatements[this.activeQuery.name] = this.activeQuery.text
|
|
291
360
|
}
|
|
292
|
-
|
|
293
|
-
process.nextTick(() => {
|
|
294
|
-
this.emit('end')
|
|
295
|
-
})
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
con.on('notice', function (msg) {
|
|
299
|
-
self.emit('notice', msg)
|
|
300
|
-
})
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
Client.prototype.connect = function (callback) {
|
|
304
|
-
if (callback) {
|
|
305
|
-
this._connect(callback)
|
|
306
|
-
return
|
|
307
361
|
}
|
|
308
362
|
|
|
309
|
-
|
|
310
|
-
this.
|
|
311
|
-
if (error) {
|
|
312
|
-
reject(error)
|
|
313
|
-
} else {
|
|
314
|
-
resolve()
|
|
315
|
-
}
|
|
316
|
-
})
|
|
317
|
-
})
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
Client.prototype._attachListeners = function (con) {
|
|
321
|
-
const self = this
|
|
322
|
-
// delegate rowDescription to active query
|
|
323
|
-
con.on('rowDescription', function (msg) {
|
|
324
|
-
self.activeQuery.handleRowDescription(msg)
|
|
325
|
-
})
|
|
326
|
-
|
|
327
|
-
// delegate dataRow to active query
|
|
328
|
-
con.on('dataRow', function (msg) {
|
|
329
|
-
self.activeQuery.handleDataRow(msg)
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
// delegate portalSuspended to active query
|
|
333
|
-
// eslint-disable-next-line no-unused-vars
|
|
334
|
-
con.on('portalSuspended', function (msg) {
|
|
335
|
-
self.activeQuery.handlePortalSuspended(con)
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
// delegate emptyQuery to active query
|
|
339
|
-
// eslint-disable-next-line no-unused-vars
|
|
340
|
-
con.on('emptyQuery', function (msg) {
|
|
341
|
-
self.activeQuery.handleEmptyQuery(con)
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
// delegate commandComplete to active query
|
|
345
|
-
con.on('commandComplete', function (msg) {
|
|
346
|
-
self.activeQuery.handleCommandComplete(msg, con)
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
// if a prepared statement has a name and properly parses
|
|
350
|
-
// we track that its already been executed so we don't parse
|
|
351
|
-
// it again on the same client
|
|
352
|
-
// eslint-disable-next-line no-unused-vars
|
|
353
|
-
con.on('parseComplete', function (msg) {
|
|
354
|
-
if (self.activeQuery.name) {
|
|
355
|
-
con.parsedStatements[self.activeQuery.name] = self.activeQuery.text
|
|
356
|
-
}
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
// eslint-disable-next-line no-unused-vars
|
|
360
|
-
con.on('copyInResponse', function (msg) {
|
|
361
|
-
self.activeQuery.handleCopyInResponse(self.connection)
|
|
362
|
-
})
|
|
363
|
-
|
|
364
|
-
con.on('copyData', function (msg) {
|
|
365
|
-
self.activeQuery.handleCopyData(msg, self.connection)
|
|
366
|
-
})
|
|
367
|
-
|
|
368
|
-
con.on('notification', function (msg) {
|
|
369
|
-
self.emit('notification', msg)
|
|
370
|
-
})
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
Client.prototype.getStartupConf = function () {
|
|
374
|
-
var params = this.connectionParameters
|
|
375
|
-
|
|
376
|
-
var data = {
|
|
377
|
-
user: params.user,
|
|
378
|
-
database: params.database,
|
|
363
|
+
_handleCopyInResponse(msg) {
|
|
364
|
+
this.activeQuery.handleCopyInResponse(this.connection)
|
|
379
365
|
}
|
|
380
366
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
data.application_name = appName
|
|
384
|
-
}
|
|
385
|
-
if (params.replication) {
|
|
386
|
-
data.replication = '' + params.replication
|
|
367
|
+
_handleCopyData(msg) {
|
|
368
|
+
this.activeQuery.handleCopyData(msg, this.connection)
|
|
387
369
|
}
|
|
388
|
-
|
|
389
|
-
|
|
370
|
+
|
|
371
|
+
_handleNotification(msg) {
|
|
372
|
+
this.emit('notification', msg)
|
|
390
373
|
}
|
|
391
|
-
|
|
392
|
-
|
|
374
|
+
|
|
375
|
+
_handleNotice(msg) {
|
|
376
|
+
this.emit('notice', msg)
|
|
393
377
|
}
|
|
394
378
|
|
|
395
|
-
|
|
396
|
-
|
|
379
|
+
getStartupConf() {
|
|
380
|
+
var params = this.connectionParameters
|
|
397
381
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
382
|
+
var data = {
|
|
383
|
+
user: params.user,
|
|
384
|
+
database: params.database,
|
|
385
|
+
}
|
|
401
386
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
387
|
+
var appName = params.application_name || params.fallback_application_name
|
|
388
|
+
if (appName) {
|
|
389
|
+
data.application_name = appName
|
|
390
|
+
}
|
|
391
|
+
if (params.replication) {
|
|
392
|
+
data.replication = '' + params.replication
|
|
393
|
+
}
|
|
394
|
+
if (params.statement_timeout) {
|
|
395
|
+
data.statement_timeout = String(parseInt(params.statement_timeout, 10))
|
|
396
|
+
}
|
|
397
|
+
if (params.idle_in_transaction_session_timeout) {
|
|
398
|
+
data.idle_in_transaction_session_timeout = String(parseInt(params.idle_in_transaction_session_timeout, 10))
|
|
399
|
+
}
|
|
400
|
+
if (params.options) {
|
|
401
|
+
data.options = params.options
|
|
406
402
|
}
|
|
407
403
|
|
|
408
|
-
|
|
409
|
-
con.on('connect', function () {
|
|
410
|
-
con.cancel(client.processID, client.secretKey)
|
|
411
|
-
})
|
|
412
|
-
} else if (client.queryQueue.indexOf(query) !== -1) {
|
|
413
|
-
client.queryQueue.splice(client.queryQueue.indexOf(query), 1)
|
|
404
|
+
return data
|
|
414
405
|
}
|
|
415
|
-
}
|
|
416
406
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
Client.prototype.getTypeParser = function (oid, format) {
|
|
422
|
-
return this._types.getTypeParser(oid, format)
|
|
423
|
-
}
|
|
407
|
+
cancel(client, query) {
|
|
408
|
+
if (client.activeQuery === query) {
|
|
409
|
+
var con = this.connection
|
|
424
410
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
411
|
+
if (this.host && this.host.indexOf('/') === 0) {
|
|
412
|
+
con.connect(this.host + '/.s.PGSQL.' + this.port)
|
|
413
|
+
} else {
|
|
414
|
+
con.connect(this.port, this.host)
|
|
415
|
+
}
|
|
429
416
|
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
var c = str[i]
|
|
437
|
-
if (c === "'") {
|
|
438
|
-
escaped += c + c
|
|
439
|
-
} else if (c === '\\') {
|
|
440
|
-
escaped += c + c
|
|
441
|
-
hasBackslash = true
|
|
442
|
-
} else {
|
|
443
|
-
escaped += c
|
|
417
|
+
// once connection is established send cancel message
|
|
418
|
+
con.on('connect', function () {
|
|
419
|
+
con.cancel(client.processID, client.secretKey)
|
|
420
|
+
})
|
|
421
|
+
} else if (client.queryQueue.indexOf(query) !== -1) {
|
|
422
|
+
client.queryQueue.splice(client.queryQueue.indexOf(query), 1)
|
|
444
423
|
}
|
|
445
424
|
}
|
|
446
425
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
if (hasBackslash === true) {
|
|
450
|
-
escaped = ' E' + escaped
|
|
426
|
+
setTypeParser(oid, format, parseFn) {
|
|
427
|
+
return this._types.setTypeParser(oid, format, parseFn)
|
|
451
428
|
}
|
|
452
429
|
|
|
453
|
-
|
|
454
|
-
|
|
430
|
+
getTypeParser(oid, format) {
|
|
431
|
+
return this._types.getTypeParser(oid, format)
|
|
432
|
+
}
|
|
455
433
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
this.readyForQuery = false
|
|
461
|
-
this.hasExecuted = true
|
|
434
|
+
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
|
|
435
|
+
escapeIdentifier(str) {
|
|
436
|
+
return '"' + str.replace(/"/g, '""') + '"'
|
|
437
|
+
}
|
|
462
438
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
439
|
+
// Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
|
|
440
|
+
escapeLiteral(str) {
|
|
441
|
+
var hasBackslash = false
|
|
442
|
+
var escaped = "'"
|
|
443
|
+
|
|
444
|
+
for (var i = 0; i < str.length; i++) {
|
|
445
|
+
var c = str[i]
|
|
446
|
+
if (c === "'") {
|
|
447
|
+
escaped += c + c
|
|
448
|
+
} else if (c === '\\') {
|
|
449
|
+
escaped += c + c
|
|
450
|
+
hasBackslash = true
|
|
451
|
+
} else {
|
|
452
|
+
escaped += c
|
|
470
453
|
}
|
|
471
|
-
} else if (this.hasExecuted) {
|
|
472
|
-
this.activeQuery = null
|
|
473
|
-
this.emit('drain')
|
|
474
454
|
}
|
|
455
|
+
|
|
456
|
+
escaped += "'"
|
|
457
|
+
|
|
458
|
+
if (hasBackslash === true) {
|
|
459
|
+
escaped = ' E' + escaped
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return escaped
|
|
475
463
|
}
|
|
476
|
-
}
|
|
477
464
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
if (!query.callback) {
|
|
498
|
-
result = new this._Promise((resolve, reject) => {
|
|
499
|
-
query.callback = (err, res) => (err ? reject(err) : resolve(res))
|
|
500
|
-
})
|
|
465
|
+
_pulseQueryQueue() {
|
|
466
|
+
if (this.readyForQuery === true) {
|
|
467
|
+
this.activeQuery = this.queryQueue.shift()
|
|
468
|
+
if (this.activeQuery) {
|
|
469
|
+
this.readyForQuery = false
|
|
470
|
+
this.hasExecuted = true
|
|
471
|
+
|
|
472
|
+
const queryError = this.activeQuery.submit(this.connection)
|
|
473
|
+
if (queryError) {
|
|
474
|
+
process.nextTick(() => {
|
|
475
|
+
this.activeQuery.handleError(queryError, this.connection)
|
|
476
|
+
this.readyForQuery = true
|
|
477
|
+
this._pulseQueryQueue()
|
|
478
|
+
})
|
|
479
|
+
}
|
|
480
|
+
} else if (this.hasExecuted) {
|
|
481
|
+
this.activeQuery = null
|
|
482
|
+
this.emit('drain')
|
|
483
|
+
}
|
|
501
484
|
}
|
|
502
485
|
}
|
|
503
486
|
|
|
504
|
-
|
|
505
|
-
|
|
487
|
+
query(config, values, callback) {
|
|
488
|
+
// can take in strings, config object or query object
|
|
489
|
+
var query
|
|
490
|
+
var result
|
|
491
|
+
var readTimeout
|
|
492
|
+
var readTimeoutTimer
|
|
493
|
+
var queryCallback
|
|
494
|
+
|
|
495
|
+
if (config === null || config === undefined) {
|
|
496
|
+
throw new TypeError('Client was passed a null or undefined query')
|
|
497
|
+
} else if (typeof config.submit === 'function') {
|
|
498
|
+
readTimeout = config.query_timeout || this.connectionParameters.query_timeout
|
|
499
|
+
result = query = config
|
|
500
|
+
if (typeof values === 'function') {
|
|
501
|
+
query.callback = query.callback || values
|
|
502
|
+
}
|
|
503
|
+
} else {
|
|
504
|
+
readTimeout = this.connectionParameters.query_timeout
|
|
505
|
+
query = new Query(config, values, callback)
|
|
506
|
+
if (!query.callback) {
|
|
507
|
+
result = new this._Promise((resolve, reject) => {
|
|
508
|
+
query.callback = (err, res) => (err ? reject(err) : resolve(res))
|
|
509
|
+
})
|
|
510
|
+
}
|
|
511
|
+
}
|
|
506
512
|
|
|
507
|
-
|
|
508
|
-
|
|
513
|
+
if (readTimeout) {
|
|
514
|
+
queryCallback = query.callback
|
|
509
515
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
516
|
+
readTimeoutTimer = setTimeout(() => {
|
|
517
|
+
var error = new Error('Query read timeout')
|
|
518
|
+
|
|
519
|
+
process.nextTick(() => {
|
|
520
|
+
query.handleError(error, this.connection)
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
queryCallback(error)
|
|
524
|
+
|
|
525
|
+
// we already returned an error,
|
|
526
|
+
// just do nothing if query completes
|
|
527
|
+
query.callback = () => {}
|
|
513
528
|
|
|
514
|
-
|
|
529
|
+
// Remove from queue
|
|
530
|
+
var index = this.queryQueue.indexOf(query)
|
|
531
|
+
if (index > -1) {
|
|
532
|
+
this.queryQueue.splice(index, 1)
|
|
533
|
+
}
|
|
515
534
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
query.callback = () => {}
|
|
535
|
+
this._pulseQueryQueue()
|
|
536
|
+
}, readTimeout)
|
|
519
537
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
this.queryQueue.splice(index, 1)
|
|
538
|
+
query.callback = (err, res) => {
|
|
539
|
+
clearTimeout(readTimeoutTimer)
|
|
540
|
+
queryCallback(err, res)
|
|
524
541
|
}
|
|
542
|
+
}
|
|
525
543
|
|
|
526
|
-
|
|
527
|
-
|
|
544
|
+
if (this.binary && !query.binary) {
|
|
545
|
+
query.binary = true
|
|
546
|
+
}
|
|
528
547
|
|
|
529
|
-
query.
|
|
530
|
-
|
|
531
|
-
queryCallback(err, res)
|
|
548
|
+
if (query._result && !query._result._types) {
|
|
549
|
+
query._result._types = this._types
|
|
532
550
|
}
|
|
533
|
-
}
|
|
534
551
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
552
|
+
if (!this._queryable) {
|
|
553
|
+
process.nextTick(() => {
|
|
554
|
+
query.handleError(new Error('Client has encountered a connection error and is not queryable'), this.connection)
|
|
555
|
+
})
|
|
556
|
+
return result
|
|
557
|
+
}
|
|
538
558
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
559
|
+
if (this._ending) {
|
|
560
|
+
process.nextTick(() => {
|
|
561
|
+
query.handleError(new Error('Client was closed and is not queryable'), this.connection)
|
|
562
|
+
})
|
|
563
|
+
return result
|
|
564
|
+
}
|
|
542
565
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
query.handleError(new Error('Client has encountered a connection error and is not queryable'), this.connection)
|
|
546
|
-
})
|
|
566
|
+
this.queryQueue.push(query)
|
|
567
|
+
this._pulseQueryQueue()
|
|
547
568
|
return result
|
|
548
569
|
}
|
|
549
570
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
query.handleError(new Error('Client was closed and is not queryable'), this.connection)
|
|
553
|
-
})
|
|
554
|
-
return result
|
|
555
|
-
}
|
|
571
|
+
end(cb) {
|
|
572
|
+
this._ending = true
|
|
556
573
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
574
|
+
// if we have never connected, then end is a noop, callback immediately
|
|
575
|
+
if (!this.connection._connecting) {
|
|
576
|
+
if (cb) {
|
|
577
|
+
cb()
|
|
578
|
+
} else {
|
|
579
|
+
return this._Promise.resolve()
|
|
580
|
+
}
|
|
581
|
+
}
|
|
561
582
|
|
|
562
|
-
|
|
563
|
-
|
|
583
|
+
if (this.activeQuery || !this._queryable) {
|
|
584
|
+
// if we have an active query we need to force a disconnect
|
|
585
|
+
// on the socket - otherwise a hung query could block end forever
|
|
586
|
+
this.connection.stream.destroy()
|
|
587
|
+
} else {
|
|
588
|
+
this.connection.end()
|
|
589
|
+
}
|
|
564
590
|
|
|
565
|
-
// if we have never connected, then end is a noop, callback immediately
|
|
566
|
-
if (!this.connection._connecting) {
|
|
567
591
|
if (cb) {
|
|
568
|
-
cb
|
|
592
|
+
this.connection.once('end', cb)
|
|
569
593
|
} else {
|
|
570
|
-
return this._Promise
|
|
594
|
+
return new this._Promise((resolve) => {
|
|
595
|
+
this.connection.once('end', resolve)
|
|
596
|
+
})
|
|
571
597
|
}
|
|
572
598
|
}
|
|
573
|
-
|
|
574
|
-
if (this.activeQuery || !this._queryable) {
|
|
575
|
-
// if we have an active query we need to force a disconnect
|
|
576
|
-
// on the socket - otherwise a hung query could block end forever
|
|
577
|
-
this.connection.stream.destroy()
|
|
578
|
-
} else {
|
|
579
|
-
this.connection.end()
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
if (cb) {
|
|
583
|
-
this.connection.once('end', cb)
|
|
584
|
-
} else {
|
|
585
|
-
return new this._Promise((resolve) => {
|
|
586
|
-
this.connection.once('end', resolve)
|
|
587
|
-
})
|
|
588
|
-
}
|
|
589
599
|
}
|
|
590
600
|
|
|
591
601
|
// expose a Query constructor
|