pg 8.0.0 → 8.1.0

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/lib/client.js CHANGED
@@ -37,7 +37,7 @@ var Client = function (config) {
37
37
  configurable: true,
38
38
  enumerable: false,
39
39
  writable: true,
40
- value: this.connectionParameters.password
40
+ value: this.connectionParameters.password,
41
41
  })
42
42
 
43
43
  this.replication = this.connectionParameters.replication
@@ -52,13 +52,15 @@ var Client = function (config) {
52
52
  this._connectionError = false
53
53
  this._queryable = true
54
54
 
55
- this.connection = c.connection || new Connection({
56
- stream: c.stream,
57
- ssl: this.connectionParameters.ssl,
58
- keepAlive: c.keepAlive || false,
59
- keepAliveInitialDelayMillis: c.keepAliveInitialDelayMillis || 0,
60
- encoding: this.connectionParameters.client_encoding || 'utf8'
61
- })
55
+ this.connection =
56
+ c.connection ||
57
+ new Connection({
58
+ stream: c.stream,
59
+ ssl: this.connectionParameters.ssl,
60
+ keepAlive: c.keepAlive || false,
61
+ keepAliveInitialDelayMillis: c.keepAliveInitialDelayMillis || 0,
62
+ encoding: this.connectionParameters.client_encoding || 'utf8',
63
+ })
62
64
  this.queryQueue = []
63
65
  this.binary = c.binary || defaults.binary
64
66
  this.processID = null
@@ -127,9 +129,10 @@ Client.prototype._connect = function (callback) {
127
129
  function checkPgPass(cb) {
128
130
  return function (msg) {
129
131
  if (typeof self.password === 'function') {
130
- self._Promise.resolve()
132
+ self._Promise
133
+ .resolve()
131
134
  .then(() => self.password())
132
- .then(pass => {
135
+ .then((pass) => {
133
136
  if (pass !== undefined) {
134
137
  if (typeof pass !== 'string') {
135
138
  con.emit('error', new TypeError('Password must be a string'))
@@ -140,7 +143,8 @@ Client.prototype._connect = function (callback) {
140
143
  self.connectionParameters.password = self.password = null
141
144
  }
142
145
  cb(msg)
143
- }).catch(err => {
146
+ })
147
+ .catch((err) => {
144
148
  con.emit('error', err)
145
149
  })
146
150
  } else if (self.password !== null) {
@@ -157,22 +161,31 @@ Client.prototype._connect = function (callback) {
157
161
  }
158
162
 
159
163
  // password request handling
160
- con.on('authenticationCleartextPassword', checkPgPass(function () {
161
- con.password(self.password)
162
- }))
164
+ con.on(
165
+ 'authenticationCleartextPassword',
166
+ checkPgPass(function () {
167
+ con.password(self.password)
168
+ })
169
+ )
163
170
 
164
171
  // password request handling
165
- con.on('authenticationMD5Password', checkPgPass(function (msg) {
166
- con.password(utils.postgresMd5PasswordHash(self.user, self.password, msg.salt))
167
- }))
172
+ con.on(
173
+ 'authenticationMD5Password',
174
+ checkPgPass(function (msg) {
175
+ con.password(utils.postgresMd5PasswordHash(self.user, self.password, msg.salt))
176
+ })
177
+ )
168
178
 
169
179
  // password request handling (SASL)
170
180
  var saslSession
171
- con.on('authenticationSASL', checkPgPass(function (msg) {
172
- saslSession = sasl.startSession(msg.mechanisms)
181
+ con.on(
182
+ 'authenticationSASL',
183
+ checkPgPass(function (msg) {
184
+ saslSession = sasl.startSession(msg.mechanisms)
173
185
 
174
- con.sendSASLInitialResponseMessage(saslSession.mechanism, saslSession.response)
175
- }))
186
+ con.sendSASLInitialResponseMessage(saslSession.mechanism, saslSession.response)
187
+ })
188
+ )
176
189
 
177
190
  // password request handling (SASL)
178
191
  con.on('authenticationSASLContinue', function (msg) {
@@ -259,9 +272,7 @@ Client.prototype._connect = function (callback) {
259
272
  })
260
273
 
261
274
  con.once('end', () => {
262
- const error = this._ending
263
- ? new Error('Connection terminated')
264
- : new Error('Connection terminated unexpectedly')
275
+ const error = this._ending ? new Error('Connection terminated') : new Error('Connection terminated unexpectedly')
265
276
 
266
277
  clearTimeout(connectionTimeoutHandle)
267
278
  this._errorAllQueries(error)
@@ -367,7 +378,7 @@ Client.prototype.getStartupConf = function () {
367
378
 
368
379
  var data = {
369
380
  user: params.user,
370
- database: params.database
381
+ database: params.database,
371
382
  }
372
383
 
373
384
  var appName = params.application_name || params.fallback_application_name
@@ -422,11 +433,11 @@ Client.prototype.escapeIdentifier = function (str) {
422
433
  // Ported from PostgreSQL 9.2.4 source code in src/interfaces/libpq/fe-exec.c
423
434
  Client.prototype.escapeLiteral = function (str) {
424
435
  var hasBackslash = false
425
- var escaped = '\''
436
+ var escaped = "'"
426
437
 
427
438
  for (var i = 0; i < str.length; i++) {
428
439
  var c = str[i]
429
- if (c === '\'') {
440
+ if (c === "'") {
430
441
  escaped += c + c
431
442
  } else if (c === '\\') {
432
443
  escaped += c + c
@@ -436,7 +447,7 @@ Client.prototype.escapeLiteral = function (str) {
436
447
  }
437
448
  }
438
449
 
439
- escaped += '\''
450
+ escaped += "'"
440
451
 
441
452
  if (hasBackslash === true) {
442
453
  escaped = ' E' + escaped
@@ -488,7 +499,7 @@ Client.prototype.query = function (config, values, callback) {
488
499
  query = new Query(config, values, callback)
489
500
  if (!query.callback) {
490
501
  result = new this._Promise((resolve, reject) => {
491
- query.callback = (err, res) => err ? reject(err) : resolve(res)
502
+ query.callback = (err, res) => (err ? reject(err) : resolve(res))
492
503
  })
493
504
  }
494
505
  }
@@ -507,7 +518,7 @@ Client.prototype.query = function (config, values, callback) {
507
518
 
508
519
  // we already returned an error,
509
520
  // just do nothing if query completes
510
- query.callback = () => { }
521
+ query.callback = () => {}
511
522
 
512
523
  // Remove from queue
513
524
  var index = this.queryQueue.indexOf(query)
@@ -555,7 +566,7 @@ Client.prototype.end = function (cb) {
555
566
  this._ending = true
556
567
 
557
568
  // if we have never connected, then end is a noop, callback immediately
558
- if (this.connection.stream.readyState === 'closed') {
569
+ if (!this.connection._connecting) {
559
570
  if (cb) {
560
571
  cb()
561
572
  } else {
@@ -11,15 +11,10 @@ var net = require('net')
11
11
  var EventEmitter = require('events').EventEmitter
12
12
  var util = require('util')
13
13
 
14
- var Writer = require('buffer-writer')
15
- // eslint-disable-next-line
16
- var PacketStream = require('pg-packet-stream')
14
+ const { parse, serialize } = require('pg-protocol')
17
15
 
18
- var TEXT_MODE = 0
19
-
20
- // TODO(bmc) support binary mode here
21
- // var BINARY_MODE = 1
22
- console.log('using faster connection')
16
+ // TODO(bmc) support binary mode at some point
17
+ console.log('***using faster connection***')
23
18
  var Connection = function (config) {
24
19
  EventEmitter.call(this)
25
20
  config = config || {}
@@ -28,15 +23,9 @@ var Connection = function (config) {
28
23
  this._keepAlive = config.keepAlive
29
24
  this._keepAliveInitialDelayMillis = config.keepAliveInitialDelayMillis
30
25
  this.lastBuffer = false
31
- this.lastOffset = 0
32
- this.buffer = null
33
- this.offset = null
34
- this.encoding = config.encoding || 'utf8'
35
26
  this.parsedStatements = {}
36
- this.writer = new Writer()
37
27
  this.ssl = config.ssl || false
38
28
  this._ending = false
39
- this._mode = TEXT_MODE
40
29
  this._emitMessage = false
41
30
  var self = this
42
31
  this.on('newListener', function (eventName) {
@@ -51,13 +40,10 @@ util.inherits(Connection, EventEmitter)
51
40
  Connection.prototype.connect = function (port, host) {
52
41
  var self = this
53
42
 
54
- if (this.stream.readyState === 'closed') {
55
- this.stream.connect(port, host)
56
- } else if (this.stream.readyState === 'open') {
57
- this.emit('connect')
58
- }
43
+ this._connecting = true
44
+ this.stream.connect(port, host)
59
45
 
60
- this.stream.on('connect', function () {
46
+ this.stream.once('connect', function () {
61
47
  if (self._keepAlive) {
62
48
  self.stream.setKeepAlive(true, self._keepAliveInitialDelayMillis)
63
49
  }
@@ -84,18 +70,23 @@ Connection.prototype.connect = function (port, host) {
84
70
  this.stream.once('data', function (buffer) {
85
71
  var responseCode = buffer.toString('utf8')
86
72
  switch (responseCode) {
87
- case 'N': // Server does not support SSL connections
88
- return self.emit('error', new Error('The server does not support SSL connections'))
89
73
  case 'S': // Server supports SSL connections, continue with a secure connection
90
74
  break
75
+ case 'N': // Server does not support SSL connections
76
+ self.stream.end()
77
+ return self.emit('error', new Error('The server does not support SSL connections'))
91
78
  default:
92
79
  // Any other response byte, including 'E' (ErrorResponse) indicating a server error
80
+ self.stream.end()
93
81
  return self.emit('error', new Error('There was an error establishing an SSL connection'))
94
82
  }
95
83
  var tls = require('tls')
96
- const options = Object.assign({
97
- socket: self.stream
98
- }, self.ssl)
84
+ const options = Object.assign(
85
+ {
86
+ socket: self.stream,
87
+ },
88
+ self.ssl
89
+ )
99
90
  if (net.isIP(host) === 0) {
100
91
  options.servername = host
101
92
  }
@@ -108,257 +99,116 @@ Connection.prototype.connect = function (port, host) {
108
99
  }
109
100
 
110
101
  Connection.prototype.attachListeners = function (stream) {
111
- var self = this
112
- const mode = this._mode === TEXT_MODE ? 'text' : 'binary'
113
- const packetStream = new PacketStream.PgPacketStream({ mode })
114
- this.stream.pipe(packetStream)
115
- packetStream.on('data', (msg) => {
102
+ stream.on('end', () => {
103
+ this.emit('end')
104
+ })
105
+ parse(stream, (msg) => {
116
106
  var eventName = msg.name === 'error' ? 'errorMessage' : msg.name
117
- if (self._emitMessage) {
118
- self.emit('message', msg)
107
+ if (this._emitMessage) {
108
+ this.emit('message', msg)
119
109
  }
120
- self.emit(eventName, msg)
121
- })
122
- stream.on('end', function () {
123
- self.emit('end')
110
+ this.emit(eventName, msg)
124
111
  })
125
112
  }
126
113
 
127
114
  Connection.prototype.requestSsl = function () {
128
- var bodyBuffer = this.writer
129
- .addInt16(0x04d2)
130
- .addInt16(0x162f)
131
- .flush()
132
-
133
- var length = bodyBuffer.length + 4
134
-
135
- var buffer = new Writer()
136
- .addInt32(length)
137
- .add(bodyBuffer)
138
- .join()
139
- this.stream.write(buffer)
115
+ this.stream.write(serialize.requestSsl())
140
116
  }
141
117
 
142
118
  Connection.prototype.startup = function (config) {
143
- var writer = this.writer.addInt16(3).addInt16(0)
144
-
145
- Object.keys(config).forEach(function (key) {
146
- var val = config[key]
147
- writer.addCString(key).addCString(val)
148
- })
149
-
150
- writer.addCString('client_encoding').addCString("'utf-8'")
151
-
152
- var bodyBuffer = writer.addCString('').flush()
153
- // this message is sent without a code
154
-
155
- var length = bodyBuffer.length + 4
156
-
157
- var buffer = new Writer()
158
- .addInt32(length)
159
- .add(bodyBuffer)
160
- .join()
161
- this.stream.write(buffer)
119
+ this.stream.write(serialize.startup(config))
162
120
  }
163
121
 
164
122
  Connection.prototype.cancel = function (processID, secretKey) {
165
- var bodyBuffer = this.writer
166
- .addInt16(1234)
167
- .addInt16(5678)
168
- .addInt32(processID)
169
- .addInt32(secretKey)
170
- .flush()
171
-
172
- var length = bodyBuffer.length + 4
173
-
174
- var buffer = new Writer()
175
- .addInt32(length)
176
- .add(bodyBuffer)
177
- .join()
178
- this.stream.write(buffer)
123
+ this._send(serialize.cancel(processID, secretKey))
179
124
  }
180
125
 
181
126
  Connection.prototype.password = function (password) {
182
- // 0x70 = 'p'
183
- this._send(0x70, this.writer.addCString(password))
127
+ this._send(serialize.password(password))
184
128
  }
185
129
 
186
130
  Connection.prototype.sendSASLInitialResponseMessage = function (mechanism, initialResponse) {
187
- // 0x70 = 'p'
188
- this.writer
189
- .addCString(mechanism)
190
- .addInt32(Buffer.byteLength(initialResponse))
191
- .addString(initialResponse)
192
-
193
- this._send(0x70)
131
+ this._send(serialize.sendSASLInitialResponseMessage(mechanism, initialResponse))
194
132
  }
195
133
 
196
134
  Connection.prototype.sendSCRAMClientFinalMessage = function (additionalData) {
197
- // 0x70 = 'p'
198
- this.writer.addString(additionalData)
199
-
200
- this._send(0x70)
135
+ this._send(serialize.sendSCRAMClientFinalMessage(additionalData))
201
136
  }
202
137
 
203
- Connection.prototype._send = function (code, more) {
138
+ Connection.prototype._send = function (buffer) {
204
139
  if (!this.stream.writable) {
205
140
  return false
206
141
  }
207
- return this.stream.write(this.writer.flush(code))
142
+ return this.stream.write(buffer)
208
143
  }
209
144
 
210
145
  Connection.prototype.query = function (text) {
211
- // 0x51 = Q
212
- this.stream.write(this.writer.addCString(text).flush(0x51))
146
+ this._send(serialize.query(text))
213
147
  }
214
148
 
215
149
  // send parse message
216
150
  Connection.prototype.parse = function (query) {
217
- // expect something like this:
218
- // { name: 'queryName',
219
- // text: 'select * from blah',
220
- // types: ['int8', 'bool'] }
221
-
222
- // normalize missing query names to allow for null
223
- query.name = query.name || ''
224
- if (query.name.length > 63) {
225
- /* eslint-disable no-console */
226
- console.error('Warning! Postgres only supports 63 characters for query names.')
227
- console.error('You supplied %s (%s)', query.name, query.name.length)
228
- console.error('This can cause conflicts and silent errors executing queries')
229
- /* eslint-enable no-console */
230
- }
231
- // normalize null type array
232
- query.types = query.types || []
233
- var len = query.types.length
234
- var buffer = this.writer
235
- .addCString(query.name) // name of query
236
- .addCString(query.text) // actual query text
237
- .addInt16(len)
238
- for (var i = 0; i < len; i++) {
239
- buffer.addInt32(query.types[i])
240
- }
241
-
242
- var code = 0x50
243
- this._send(code)
244
- this.flush()
151
+ this._send(serialize.parse(query))
245
152
  }
246
153
 
247
154
  // send bind message
248
155
  // "more" === true to buffer the message until flush() is called
249
156
  Connection.prototype.bind = function (config) {
250
- // normalize config
251
- config = config || {}
252
- config.portal = config.portal || ''
253
- config.statement = config.statement || ''
254
- config.binary = config.binary || false
255
- var values = config.values || []
256
- var len = values.length
257
- var useBinary = false
258
- for (var j = 0; j < len; j++) {
259
- useBinary |= values[j] instanceof Buffer
260
- }
261
- var buffer = this.writer.addCString(config.portal).addCString(config.statement)
262
- if (!useBinary) {
263
- buffer.addInt16(0)
264
- } else {
265
- buffer.addInt16(len)
266
- for (j = 0; j < len; j++) {
267
- buffer.addInt16(values[j] instanceof Buffer)
268
- }
269
- }
270
- buffer.addInt16(len)
271
- for (var i = 0; i < len; i++) {
272
- var val = values[i]
273
- if (val === null || typeof val === 'undefined') {
274
- buffer.addInt32(-1)
275
- } else if (val instanceof Buffer) {
276
- buffer.addInt32(val.length)
277
- buffer.add(val)
278
- } else {
279
- buffer.addInt32(Buffer.byteLength(val))
280
- buffer.addString(val)
281
- }
282
- }
283
-
284
- if (config.binary) {
285
- buffer.addInt16(1) // format codes to use binary
286
- buffer.addInt16(1)
287
- } else {
288
- buffer.addInt16(0) // format codes to use text
289
- }
290
- // 0x42 = 'B'
291
- this._send(0x42)
292
- this.flush()
157
+ this._send(serialize.bind(config))
293
158
  }
294
159
 
295
160
  // send execute message
296
161
  // "more" === true to buffer the message until flush() is called
297
162
  Connection.prototype.execute = function (config) {
298
- config = config || {}
299
- config.portal = config.portal || ''
300
- config.rows = config.rows || ''
301
- this.writer.addCString(config.portal).addInt32(config.rows)
302
-
303
- // 0x45 = 'E'
304
- this._send(0x45)
305
- this.flush()
163
+ this._send(serialize.execute(config))
306
164
  }
307
165
 
308
- var emptyBuffer = Buffer.alloc(0)
309
-
310
- const flushBuffer = Buffer.from([0x48, 0x00, 0x00, 0x00, 0x04])
166
+ const flushBuffer = serialize.flush()
311
167
  Connection.prototype.flush = function () {
312
168
  if (this.stream.writable) {
313
169
  this.stream.write(flushBuffer)
314
170
  }
315
171
  }
316
172
 
317
- const syncBuffer = Buffer.from([0x53, 0x00, 0x00, 0x00, 0x04])
173
+ const syncBuffer = serialize.sync()
318
174
  Connection.prototype.sync = function () {
319
175
  this._ending = true
320
- // clear out any pending data in the writer
321
- this.writer.clear()
322
- if (this.stream.writable) {
323
- this.stream.write(syncBuffer)
324
- this.stream.write(flushBuffer)
325
- }
176
+ this._send(syncBuffer)
177
+ this._send(flushBuffer)
326
178
  }
327
179
 
328
- const END_BUFFER = Buffer.from([0x58, 0x00, 0x00, 0x00, 0x04])
180
+ const endBuffer = serialize.end()
329
181
 
330
182
  Connection.prototype.end = function () {
331
183
  // 0x58 = 'X'
332
- this.writer.clear()
333
184
  this._ending = true
334
- return this.stream.write(END_BUFFER, () => {
185
+ if (!this._connecting || !this.stream.writable) {
186
+ this.stream.end()
187
+ return
188
+ }
189
+ return this.stream.write(endBuffer, () => {
335
190
  this.stream.end()
336
191
  })
337
192
  }
338
193
 
339
194
  Connection.prototype.close = function (msg) {
340
- this.writer.addCString(msg.type + (msg.name || ''))
341
- this._send(0x43)
195
+ this._send(serialize.close(msg))
342
196
  }
343
197
 
344
198
  Connection.prototype.describe = function (msg) {
345
- this.writer.addCString(msg.type + (msg.name || ''))
346
- this._send(0x44)
347
- this.flush()
199
+ this._send(serialize.describe(msg))
348
200
  }
349
201
 
350
202
  Connection.prototype.sendCopyFromChunk = function (chunk) {
351
- this.stream.write(this.writer.add(chunk).flush(0x64))
203
+ this._send(serialize.copyData(chunk))
352
204
  }
353
205
 
354
206
  Connection.prototype.endCopyFrom = function () {
355
- this.stream.write(this.writer.add(emptyBuffer).flush(0x63))
207
+ this._send(serialize.copyDone())
356
208
  }
357
209
 
358
210
  Connection.prototype.sendCopyFail = function (msg) {
359
- // this.stream.write(this.writer.add(emptyBuffer).flush(0x66));
360
- this.writer.addCString(msg)
361
- this._send(0x66)
211
+ this._send(serialize.copyFail(msg))
362
212
  }
363
213
 
364
214
  module.exports = Connection
@@ -22,12 +22,10 @@ var val = function (key, config, envVar) {
22
22
  envVar = process.env[envVar]
23
23
  }
24
24
 
25
- return config[key] ||
26
- envVar ||
27
- defaults[key]
25
+ return config[key] || envVar || defaults[key]
28
26
  }
29
27
 
30
- var useSsl = function () {
28
+ var readSSLConfigFromEnvironment = function () {
31
29
  switch (process.env.PGSSLMODE) {
32
30
  case 'disable':
33
31
  return false
@@ -36,6 +34,8 @@ var useSsl = function () {
36
34
  case 'verify-ca':
37
35
  case 'verify-full':
38
36
  return true
37
+ case 'no-verify':
38
+ return { rejectUnauthorized: false }
39
39
  }
40
40
  return defaults.ssl
41
41
  }
@@ -66,15 +66,22 @@ var ConnectionParameters = function (config) {
66
66
  configurable: true,
67
67
  enumerable: false,
68
68
  writable: true,
69
- value: val('password', config)
69
+ value: val('password', config),
70
70
  })
71
71
 
72
72
  this.binary = val('binary', config)
73
- this.ssl = typeof config.ssl === 'undefined' ? useSsl() : config.ssl
73
+
74
+ this.ssl = typeof config.ssl === 'undefined' ? readSSLConfigFromEnvironment() : config.ssl
75
+
76
+ // support passing in ssl=no-verify via connection string
77
+ if (this.ssl === 'no-verify') {
78
+ this.ssl = { rejectUnauthorized: false }
79
+ }
80
+
74
81
  this.client_encoding = val('client_encoding', config)
75
82
  this.replication = val('replication', config)
76
83
  // a domain socket begins with '/'
77
- this.isDomainSocket = (!(this.host || '').indexOf('/'))
84
+ this.isDomainSocket = !(this.host || '').indexOf('/')
78
85
 
79
86
  this.application_name = val('application_name', config, 'PGAPPNAME')
80
87
  this.fallback_application_name = val('fallback_application_name', config, false)