pg 8.3.0 → 8.3.2
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/LICENSE +21 -0
- package/README.md +1 -1
- package/lib/client.js +474 -467
- package/lib/connection-parameters.js +104 -109
- package/lib/connection.js +168 -173
- package/lib/defaults.js +0 -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 +3 -2
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,576 +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, function (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
|
|
387
|
-
}
|
|
388
|
-
if (params.statement_timeout) {
|
|
389
|
-
data.statement_timeout = String(parseInt(params.statement_timeout, 10))
|
|
367
|
+
_handleCopyData(msg) {
|
|
368
|
+
this.activeQuery.handleCopyData(msg, this.connection)
|
|
390
369
|
}
|
|
391
|
-
|
|
392
|
-
|
|
370
|
+
|
|
371
|
+
_handleNotification(msg) {
|
|
372
|
+
this.emit('notification', msg)
|
|
393
373
|
}
|
|
394
|
-
|
|
395
|
-
|
|
374
|
+
|
|
375
|
+
_handleNotice(msg) {
|
|
376
|
+
this.emit('notice', msg)
|
|
396
377
|
}
|
|
397
378
|
|
|
398
|
-
|
|
399
|
-
|
|
379
|
+
getStartupConf() {
|
|
380
|
+
var params = this.connectionParameters
|
|
400
381
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
382
|
+
var data = {
|
|
383
|
+
user: params.user,
|
|
384
|
+
database: params.database,
|
|
385
|
+
}
|
|
404
386
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
|
409
402
|
}
|
|
410
403
|
|
|
411
|
-
|
|
412
|
-
con.on('connect', function () {
|
|
413
|
-
con.cancel(client.processID, client.secretKey)
|
|
414
|
-
})
|
|
415
|
-
} else if (client.queryQueue.indexOf(query) !== -1) {
|
|
416
|
-
client.queryQueue.splice(client.queryQueue.indexOf(query), 1)
|
|
404
|
+
return data
|
|
417
405
|
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
Client.prototype.setTypeParser = function (oid, format, parseFn) {
|
|
421
|
-
return this._types.setTypeParser(oid, format, parseFn)
|
|
422
|
-
}
|
|
423
406
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
407
|
+
cancel(client, query) {
|
|
408
|
+
if (client.activeQuery === query) {
|
|
409
|
+
var con = this.connection
|
|
427
410
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
+
}
|
|
432
416
|
|
|
433
|
-
//
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
var c = str[i]
|
|
440
|
-
if (c === "'") {
|
|
441
|
-
escaped += c + c
|
|
442
|
-
} else if (c === '\\') {
|
|
443
|
-
escaped += c + c
|
|
444
|
-
hasBackslash = true
|
|
445
|
-
} else {
|
|
446
|
-
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)
|
|
447
423
|
}
|
|
448
424
|
}
|
|
449
425
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
if (hasBackslash === true) {
|
|
453
|
-
escaped = ' E' + escaped
|
|
426
|
+
setTypeParser(oid, format, parseFn) {
|
|
427
|
+
return this._types.setTypeParser(oid, format, parseFn)
|
|
454
428
|
}
|
|
455
429
|
|
|
456
|
-
|
|
457
|
-
|
|
430
|
+
getTypeParser(oid, format) {
|
|
431
|
+
return this._types.getTypeParser(oid, format)
|
|
432
|
+
}
|
|
458
433
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
this.readyForQuery = false
|
|
464
|
-
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
|
+
}
|
|
465
438
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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
|
|
473
453
|
}
|
|
474
|
-
} else if (this.hasExecuted) {
|
|
475
|
-
this.activeQuery = null
|
|
476
|
-
this.emit('drain')
|
|
477
454
|
}
|
|
455
|
+
|
|
456
|
+
escaped += "'"
|
|
457
|
+
|
|
458
|
+
if (hasBackslash === true) {
|
|
459
|
+
escaped = ' E' + escaped
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return escaped
|
|
478
463
|
}
|
|
479
|
-
}
|
|
480
464
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if (!query.callback) {
|
|
501
|
-
result = new this._Promise((resolve, reject) => {
|
|
502
|
-
query.callback = (err, res) => (err ? reject(err) : resolve(res))
|
|
503
|
-
})
|
|
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
|
+
}
|
|
504
484
|
}
|
|
505
485
|
}
|
|
506
486
|
|
|
507
|
-
|
|
508
|
-
|
|
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
|
+
}
|
|
509
512
|
|
|
510
|
-
|
|
511
|
-
|
|
513
|
+
if (readTimeout) {
|
|
514
|
+
queryCallback = query.callback
|
|
512
515
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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 = () => {}
|
|
516
528
|
|
|
517
|
-
|
|
529
|
+
// Remove from queue
|
|
530
|
+
var index = this.queryQueue.indexOf(query)
|
|
531
|
+
if (index > -1) {
|
|
532
|
+
this.queryQueue.splice(index, 1)
|
|
533
|
+
}
|
|
518
534
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
query.callback = () => {}
|
|
535
|
+
this._pulseQueryQueue()
|
|
536
|
+
}, readTimeout)
|
|
522
537
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
this.queryQueue.splice(index, 1)
|
|
538
|
+
query.callback = (err, res) => {
|
|
539
|
+
clearTimeout(readTimeoutTimer)
|
|
540
|
+
queryCallback(err, res)
|
|
527
541
|
}
|
|
542
|
+
}
|
|
528
543
|
|
|
529
|
-
|
|
530
|
-
|
|
544
|
+
if (this.binary && !query.binary) {
|
|
545
|
+
query.binary = true
|
|
546
|
+
}
|
|
531
547
|
|
|
532
|
-
query.
|
|
533
|
-
|
|
534
|
-
queryCallback(err, res)
|
|
548
|
+
if (query._result && !query._result._types) {
|
|
549
|
+
query._result._types = this._types
|
|
535
550
|
}
|
|
536
|
-
}
|
|
537
551
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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
|
+
}
|
|
541
558
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
+
}
|
|
545
565
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
query.handleError(new Error('Client has encountered a connection error and is not queryable'), this.connection)
|
|
549
|
-
})
|
|
566
|
+
this.queryQueue.push(query)
|
|
567
|
+
this._pulseQueryQueue()
|
|
550
568
|
return result
|
|
551
569
|
}
|
|
552
570
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
query.handleError(new Error('Client was closed and is not queryable'), this.connection)
|
|
556
|
-
})
|
|
557
|
-
return result
|
|
558
|
-
}
|
|
571
|
+
end(cb) {
|
|
572
|
+
this._ending = true
|
|
559
573
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
+
}
|
|
564
582
|
|
|
565
|
-
|
|
566
|
-
|
|
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
|
+
}
|
|
567
590
|
|
|
568
|
-
// if we have never connected, then end is a noop, callback immediately
|
|
569
|
-
if (!this.connection._connecting) {
|
|
570
591
|
if (cb) {
|
|
571
|
-
cb
|
|
592
|
+
this.connection.once('end', cb)
|
|
572
593
|
} else {
|
|
573
|
-
return this._Promise
|
|
594
|
+
return new this._Promise((resolve) => {
|
|
595
|
+
this.connection.once('end', resolve)
|
|
596
|
+
})
|
|
574
597
|
}
|
|
575
598
|
}
|
|
576
|
-
|
|
577
|
-
if (this.activeQuery || !this._queryable) {
|
|
578
|
-
// if we have an active query we need to force a disconnect
|
|
579
|
-
// on the socket - otherwise a hung query could block end forever
|
|
580
|
-
this.connection.stream.destroy()
|
|
581
|
-
} else {
|
|
582
|
-
this.connection.end()
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
if (cb) {
|
|
586
|
-
this.connection.once('end', cb)
|
|
587
|
-
} else {
|
|
588
|
-
return new this._Promise((resolve) => {
|
|
589
|
-
this.connection.once('end', resolve)
|
|
590
|
-
})
|
|
591
|
-
}
|
|
592
599
|
}
|
|
593
600
|
|
|
594
601
|
// expose a Query constructor
|