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