@steadybrew/postgresjs 3.4.9 → 4.0.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.
@@ -4,7 +4,7 @@ const crypto = require('crypto')
4
4
  const Stream = require('stream')
5
5
  const { performance } = require('perf_hooks')
6
6
 
7
- const { stringify, handleValue, arrayParser, arraySerializer } = require('./types.js')
7
+ const { stringify, handleValue, addArrayType } = require('./types.js')
8
8
  const { Errors } = require('./errors.js')
9
9
  const Result = require('./result.js')
10
10
  const Queue = require('./queue.js')
@@ -22,6 +22,8 @@ const Sync = b().S().end()
22
22
  , DescribeUnnamed = b().D().str('S').str(b.N).end()
23
23
  , noop = () => { /* noop */ }
24
24
 
25
+ const Phase = { Closed: 0, Backoff: 1, Opening: 2, Negotiating: 3, Authenticating: 4, Initializing: 5, Ready: 6, Draining: 7, Closing: 8 }
26
+
25
27
  const retryRoutines = new Set([
26
28
  'FetchPreparedStatement',
27
29
  'RevalidateCachedQuery',
@@ -49,7 +51,7 @@ const errorFields = {
49
51
  82 : 'routine' // R
50
52
  }
51
53
 
52
- function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose = noop } = {}) {
54
+ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose = noop, ondrain = noop } = {}) {
53
55
  const {
54
56
  sslnegotiation,
55
57
  ssl,
@@ -74,10 +76,16 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
74
76
  , backend = { pid: null, secret: null }
75
77
  , idleTimer = timer(end, options.idle_timeout)
76
78
  , lifeTimer = timer(end, options.max_lifetime)
77
- , connectTimer = timer(connectTimedOut, options.connect_timeout)
78
79
 
79
80
  let socket = null
80
- , cancelMessage
81
+ , phase = Phase.Closed
82
+ , generation = 0
83
+ , acquisition = null
84
+ , endRequested = false
85
+ , endWaiters = []
86
+ , inheritedBackoff = null
87
+ , backoffTimer = null
88
+ , connectTimer = null
81
89
  , errorResponse = null
82
90
  , result = new Result()
83
91
  , incoming = Buffer.alloc(0)
@@ -86,23 +94,16 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
86
94
  , statements = {}
87
95
  , statementId = Math.random().toString(36).slice(2)
88
96
  , statementCount = 1
89
- , closedTime = 0
90
97
  , remaining = 0
91
98
  , hostIndex = 0
92
- , retries = 0
93
99
  , length = 0
94
- , delay = 0
95
100
  , rows = 0
96
101
  , serverSignature = null
97
102
  , nextWriteTimer = null
98
- , terminated = false
99
103
  , incomings = null
100
104
  , results = null
101
- , initial = null
102
- , ending = null
103
105
  , stream = null
104
106
  , chunk = null
105
- , ended = null
106
107
  , nonce = null
107
108
  , query = null
108
109
  , final = null
@@ -110,13 +111,11 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
110
111
  const connection = {
111
112
  queue: queues.closed,
112
113
  idleTimer,
113
- connect(query) {
114
- initial = query
115
- reconnect()
116
- },
114
+ connect: acquire,
117
115
  terminate,
118
116
  execute,
119
117
  cancel,
118
+ release,
120
119
  end,
121
120
  count: 0,
122
121
  id
@@ -126,36 +125,320 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
126
125
 
127
126
  return connection
128
127
 
129
- async function createSocket() {
130
- let x
128
+ function transition(next) {
129
+ generation++
130
+ phase = next
131
+ }
132
+
133
+ function starting() {
134
+ return phase >= Phase.Opening && phase <= Phase.Initializing
135
+ }
136
+
137
+ function held() {
138
+ return connection.reserved || connection.queue === queues.reserved
139
+ }
140
+
141
+ function drained() {
142
+ ondrain(connection) || closing(true)
143
+ }
144
+
145
+ function idle() {
146
+ return !query && sent.length === 0
147
+ }
148
+
149
+ function backoffMs() {
150
+ return (typeof backoff === 'function' ? backoff(options.shared.retries) : backoff) * 1000
151
+ }
152
+
153
+ function acquire(owner) {
154
+ if (phase !== Phase.Closed)
155
+ return queryError(owner, Errors.connection('CONNECTION_CLOSED', options))
156
+
157
+ acquisition = { owner, attempts: 0, hostsTried: 0, lastError: null, deadline: null, expired: false }
158
+ const wait = inheritedBackoff ? inheritedBackoff.at + inheritedBackoff.delay - performance.now() : 0
159
+ inheritedBackoff = null
160
+ wait > 0 ? enterBackoff(wait) : enterOpening()
161
+ }
162
+
163
+ function enterBackoff(ms) {
164
+ transition(Phase.Backoff)
165
+ const a = acquisition
166
+ const attempt = generation
167
+ const left = a.deadline === null ? Infinity : Math.max(0, a.deadline - performance.now())
168
+ a.expired = left <= ms
169
+ backoffTimer = setTimeout(() => {
170
+ backoffTimer = null
171
+ attempt === generation && (a.expired
172
+ ? enterClosed(a.lastError || Errors.connection('CONNECT_TIMEOUT', options))
173
+ : enterOpening())
174
+ }, Math.min(ms, left))
175
+ }
176
+
177
+ function enterOpening() {
178
+ transition(Phase.Opening)
179
+ const a = acquisition
180
+ const attempt = generation
181
+ const ms = options.connect_timeout * 1000
182
+ if (ms) {
183
+ const now = performance.now()
184
+ a.deadline === null && (a.deadline = now + ms * host.length)
185
+ const left = a.deadline - now
186
+ connectTimer = setTimeout(() => connectTimedOut(left <= ms), Math.min(ms, left))
187
+ }
188
+ a.hostsTried++
189
+ backendParameters = {}
190
+ Promise.resolve()
191
+ .then(() => options.socket ? options.socket(options) : new net.Socket())
192
+ .then(created => attempt === generation ? attach(created) : dispose(created))
193
+ .catch(err => attempt === generation && fail(err))
194
+ }
195
+
196
+ function dispose(created) {
197
+ created.on('error', noop)
198
+ created.destroy()
199
+ }
200
+
201
+ function attach(created) {
202
+ socket = created
203
+ created.on('error', error)
204
+ created.on('close', closed)
205
+ created.on('drain', drain)
206
+
207
+ if (options.socket)
208
+ return ssl ? negotiate() : authenticate()
209
+
210
+ created.on('connect', ssl ? negotiate : authenticate)
211
+
212
+ if (options.path)
213
+ return created.connect(options.path)
214
+
215
+ created.ssl = ssl
216
+ created.connect(port[hostIndex], host[hostIndex])
217
+ created.host = host[hostIndex]
218
+ created.port = port[hostIndex]
219
+
220
+ hostIndex = (hostIndex + 1) % port.length
221
+ }
222
+
223
+ function negotiate() {
224
+ transition(Phase.Negotiating)
225
+ if (sslnegotiation === 'direct')
226
+ return upgrade()
227
+
228
+ const attempt = generation
229
+ socket.once('data', x => attempt === generation && (
230
+ x[0] === 83
231
+ ? upgrade()
232
+ : ssl === 'prefer'
233
+ ? authenticate()
234
+ : fail(Errors.generic('SSL_NOT_SUPPORTED', 'The server does not support SSL connections'))
235
+ ))
236
+ write(SSLRequest)
237
+ }
238
+
239
+ function upgrade() {
240
+ try {
241
+ const raw = socket
242
+ const config = tlsConfig(raw)
243
+ raw.removeAllListeners()
244
+ socket = tls.connect(config)
245
+ socket.on('secureConnect', authenticate)
246
+ socket.on('error', error)
247
+ socket.on('close', closed)
248
+ socket.on('drain', drain)
249
+ } catch (err) {
250
+ fail(err)
251
+ }
252
+ }
253
+
254
+ function tlsConfig(raw) {
255
+ const config = {
256
+ socket: raw,
257
+ servername: net.isIP(raw.host) ? undefined : raw.host
258
+ }
259
+
260
+ if (sslnegotiation === 'direct')
261
+ config.ALPNProtocols = ['postgresql']
262
+
263
+ if (ssl === 'require' || ssl === 'allow' || ssl === 'prefer')
264
+ config.rejectUnauthorized = false
265
+ else if (typeof ssl === 'object')
266
+ Object.assign(config, ssl)
267
+
268
+ return config
269
+ }
270
+
271
+ function authenticate() {
272
+ transition(Phase.Authenticating)
131
273
  try {
132
- x = options.socket
133
- ? (await Promise.resolve(options.socket(options)))
134
- : new net.Socket()
135
- } catch (e) {
136
- error(e)
274
+ statements = {}
275
+ needsTypes = options.fetch_types
276
+ statementId = Math.random().toString(36).slice(2)
277
+ statementCount = 1
278
+ lifeTimer.start()
279
+ socket.on('data', data)
280
+ keep_alive && socket.setKeepAlive && socket.setKeepAlive(true, 1000 * keep_alive)
281
+ write(StartupMessage())
282
+ } catch (err) {
283
+ fail(err)
284
+ }
285
+ }
286
+
287
+ function connectTimedOut(expiring) {
288
+ connectTimer = null
289
+ if (!starting())
137
290
  return
291
+ expiring && (acquisition.expired = true)
292
+ fail(Errors.connection('CONNECT_TIMEOUT', options, socket), true)
293
+ }
294
+
295
+ function fail(err, persist = host.length > 1) {
296
+ const a = acquisition
297
+ a.attempts++
298
+ a.lastError = err && err.code === 'CONNECT_TIMEOUT' && a.lastError ? a.lastError : err
299
+ clearTimeout(connectTimer)
300
+ connectTimer = null
301
+ detach()
302
+ resetSocketState(err)
303
+
304
+ if (endRequested)
305
+ return enterClosed(err)
306
+
307
+ if (a.expired || (a.deadline !== null && performance.now() >= a.deadline))
308
+ return enterClosed(a.lastError)
309
+
310
+ if (a.hostsTried < host.length)
311
+ return enterBackoff(0)
312
+
313
+ options.shared.retries++
314
+ if (!persist) {
315
+ inheritedBackoff = { at: performance.now(), delay: backoffMs() }
316
+ return enterClosed(err)
317
+ }
318
+
319
+ a.hostsTried = 0
320
+ enterBackoff(backoffMs())
321
+ }
322
+
323
+ function detach() {
324
+ clearTimeout(connectTimer)
325
+ connectTimer = null
326
+ idleTimer.cancel()
327
+ lifeTimer.cancel()
328
+ incoming = Buffer.alloc(0)
329
+ remaining = 0
330
+ incomings = null
331
+ if (socket) {
332
+ socket.removeAllListeners()
333
+ socket.on('error', noop)
334
+ socket.destroy()
335
+ socket = null
138
336
  }
139
- x.on('error', error)
140
- x.on('close', closed)
141
- x.on('drain', drain)
142
- return x
337
+ }
338
+
339
+ function enterClosed(err = Errors.connection('CONNECTION_CLOSED', options, socket)) {
340
+ if (phase === Phase.Closed)
341
+ return
342
+
343
+ const a = acquisition
344
+ const waiters = endWaiters
345
+ clearTimeout(backoffTimer)
346
+ backoffTimer = null
347
+ detach()
348
+ resetSocketState(err)
349
+ acquisition = null
350
+ endRequested = false
351
+ endWaiters = []
352
+ transition(Phase.Closed)
353
+ a && queryError(a.owner, err)
354
+ waiters.forEach(resolve => resolve())
355
+ onclose(connection, err)
356
+ }
357
+
358
+ function closing(quit) {
359
+ transition(Phase.Closing)
360
+ idleTimer.cancel()
361
+ lifeTimer.cancel()
362
+ quit && socket.readyState === 'open'
363
+ ? socket.end(b().X().end())
364
+ : socket.destroy()
365
+ }
366
+
367
+ function handoff() {
368
+ const owner = acquisition.owner
369
+ transition(Phase.Ready)
370
+ clearTimeout(connectTimer)
371
+ connectTimer = null
372
+ acquisition = null
373
+ options.shared.retries = 0
374
+
375
+ if (endRequested) {
376
+ endRequested = false
377
+ if (owner.reserve)
378
+ return (queryError(owner, Errors.connection('CONNECTION_ENDED', options)), closing(true))
379
+ if (owner.cancelled)
380
+ return closing(true)
381
+ transition(Phase.Draining)
382
+ return execute(owner)
383
+ }
384
+
385
+ owner.reserve
386
+ ? onopen(connection, owner)
387
+ : owner.cancelled
388
+ ? onopen(connection)
389
+ : execute(owner)
143
390
  }
144
391
 
145
392
  async function cancel({ pid, secret }, resolve, reject) {
393
+ let timeout = null
146
394
  try {
147
- cancelMessage = b().i32(16).i32(80877102).i32(pid).i32(secret).end(16)
148
- await connect()
149
- socket.once('error', reject)
150
- socket.once('close', resolve)
395
+ let s = await Promise.resolve(options.socket ? options.socket(options) : new net.Socket())
396
+ const request = b().i32(16).i32(80877102).i32(pid).i32(secret).end(16)
397
+ const watch = x => {
398
+ x.once('error', reject)
399
+ x.on('error', noop)
400
+ x.once('close', () => (clearTimeout(timeout), resolve()))
401
+ }
402
+ const upgrade = () => {
403
+ const raw = s
404
+ const config = tlsConfig(raw)
405
+ raw.removeAllListeners()
406
+ s = tls.connect(config)
407
+ watch(s)
408
+ s.once('secureConnect', () => s.write(request))
409
+ }
410
+ const start = !ssl
411
+ ? () => s.write(request)
412
+ : sslnegotiation === 'direct'
413
+ ? upgrade
414
+ : () => {
415
+ s.once('data', x => x[0] === 83 || ssl !== 'prefer' ? upgrade() : s.write(request))
416
+ s.write(SSLRequest)
417
+ }
418
+
419
+ watch(s)
420
+ options.connect_timeout && (timeout = setTimeout(() => s.destroy(), options.connect_timeout * 1000))
421
+
422
+ if (options.socket)
423
+ return start()
424
+
425
+ s.once('connect', start)
426
+
427
+ if (options.path)
428
+ return s.connect(options.path)
429
+
430
+ s.ssl = ssl
431
+ s.connect(port[0], host[0])
432
+ s.host = host[0]
433
+ s.port = port[0]
151
434
  } catch (error) {
152
435
  reject(error)
153
436
  }
154
437
  }
155
438
 
156
439
  function execute(q) {
157
- if (terminated)
158
- return queryError(q, Errors.connection('CONNECTION_DESTROYED', options))
440
+ if (phase !== Phase.Ready && phase !== Phase.Draining && !(phase === Phase.Initializing && q.initialization))
441
+ return queryError(q, Errors.connection('CONNECTION_CLOSED', options))
159
442
 
160
443
  if (stream)
161
444
  return queryError(q, Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy'))
@@ -174,7 +457,6 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
174
457
  && !q.describeFirst
175
458
  && !q.cursorFn
176
459
  && sent.length < max_pipeline
177
- && (!q.options.onexecute || q.options.onexecute(connection))
178
460
  } catch (error) {
179
461
  sent.length === 0 && write(Sync)
180
462
  errored(error)
@@ -252,50 +534,28 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
252
534
  }
253
535
 
254
536
  function nextWrite(fn) {
255
- const x = socket.write(chunk, fn)
537
+ const x = socket ? socket.write(chunk, fn) : false
256
538
  nextWriteTimer !== null && clearImmediate(nextWriteTimer)
257
539
  chunk = nextWriteTimer = null
258
540
  return x
259
541
  }
260
542
 
261
- function connectTimedOut() {
262
- errored(Errors.connection('CONNECT_TIMEOUT', options, socket))
263
- socket.destroy()
264
- }
265
-
266
- async function secure() {
267
- if (sslnegotiation !== 'direct') {
268
- write(SSLRequest)
269
- const canSSL = await new Promise(r => socket.once('data', x => r(x[0] === 83))) // S
270
-
271
- if (!canSSL && ssl === 'prefer')
272
- return connected()
273
- }
274
-
275
- const options = {
276
- socket,
277
- servername: net.isIP(socket.host) ? undefined : socket.host
278
- }
279
-
280
- if (sslnegotiation === 'direct')
281
- options.ALPNProtocols = ['postgresql']
282
-
283
- if (ssl === 'require' || ssl === 'allow' || ssl === 'prefer')
284
- options.rejectUnauthorized = false
285
- else if (typeof ssl === 'object')
286
- Object.assign(options, ssl)
287
-
288
- socket.removeAllListeners()
289
- socket = tls.connect(options)
290
- socket.on('secureConnect', connected)
291
- socket.on('error', error)
292
- socket.on('close', closed)
293
- socket.on('drain', drain)
543
+ function resetSocketState(err) {
544
+ clearImmediate(nextWriteTimer)
545
+ stream && (stream.destroy(err), stream = null)
546
+ final && (final(err), final = null)
547
+ query && queryError(query, err)
548
+ while (sent.length)
549
+ queryError(sent.shift(), err)
550
+ query = results = errorResponse = chunk = nextWriteTimer = null
551
+ result = new Result()
552
+ rows = 0
553
+ nonce = serverSignature = null
294
554
  }
295
555
 
296
556
  /* c8 ignore next 3 */
297
557
  function drain() {
298
- !query && onopen(connection)
558
+ phase === Phase.Ready && !query && !held() && onopen(connection)
299
559
  }
300
560
 
301
561
  function data(x) {
@@ -312,6 +572,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
312
572
  ? x
313
573
  : Buffer.concat([incoming, x], incoming.length + x.length)
314
574
 
575
+ const source = socket
315
576
  while (incoming.length > 4) {
316
577
  length = incoming.readUInt32BE(1)
317
578
  if (length >= incoming.length) {
@@ -324,73 +585,37 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
324
585
  handle(incoming.subarray(0, length + 1))
325
586
  } catch (e) {
326
587
  query && (query.cursorFn || query.describeFirst) && write(Sync)
327
- errored(e)
588
+ starting() ? fail(e) : errored(e)
328
589
  }
590
+ if (socket !== source)
591
+ return
329
592
  incoming = incoming.subarray(length + 1)
330
593
  remaining = 0
331
594
  incomings = null
332
595
  }
333
596
  }
334
597
 
335
- async function connect() {
336
- terminated = false
337
- backendParameters = {}
338
- socket || (socket = await createSocket())
339
-
340
- if (!socket)
341
- return
342
-
343
- connectTimer.start()
344
-
345
- if (options.socket)
346
- return ssl ? secure() : connected()
347
-
348
- socket.on('connect', ssl ? secure : connected)
349
-
350
- if (options.path)
351
- return socket.connect(options.path)
352
-
353
- socket.ssl = ssl
354
- socket.connect(port[hostIndex], host[hostIndex])
355
- socket.host = host[hostIndex]
356
- socket.port = port[hostIndex]
357
-
358
- hostIndex = (hostIndex + 1) % port.length
359
- }
598
+ function error(err) {
599
+ if (starting())
600
+ return fail(err)
360
601
 
361
- function reconnect() {
362
- setTimeout(connect, closedTime ? Math.max(0, closedTime + delay - performance.now()) : 0)
602
+ if (phase === Phase.Ready || phase === Phase.Draining)
603
+ socketFailed(err)
363
604
  }
364
605
 
365
- function connected() {
366
- try {
367
- statements = {}
368
- needsTypes = options.fetch_types
369
- statementId = Math.random().toString(36).slice(2)
370
- statementCount = 1
371
- lifeTimer.start()
372
- socket.on('data', data)
373
- keep_alive && socket.setKeepAlive && socket.setKeepAlive(true, 1000 * keep_alive)
374
- const s = StartupMessage()
375
- write(s)
376
- } catch (err) {
377
- error(err)
378
- }
379
- }
380
-
381
- function error(err) {
382
- if (connection.queue === queues.connecting && options.host[retries + 1])
383
- return
384
-
606
+ function socketFailed(err) {
385
607
  errored(err)
386
608
  while (sent.length)
387
609
  queryError(sent.shift(), err)
610
+ query = null
611
+ options.shared.retries++
612
+ inheritedBackoff = { at: performance.now(), delay: backoffMs() }
613
+ closing(false)
388
614
  }
389
615
 
390
616
  function errored(err) {
391
617
  stream && (stream.destroy(err), stream = null)
392
618
  query && queryError(query, err)
393
- initial && (queryError(initial, err), initial = null)
394
619
  }
395
620
 
396
621
  function queryError(query, err) {
@@ -411,52 +636,67 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
411
636
  }
412
637
 
413
638
  function end() {
414
- return ending || (
415
- !connection.reserved && onend(connection),
416
- !connection.reserved && !initial && !query && sent.length === 0
417
- ? (terminate(), new Promise(r => socket && socket.readyState !== 'closed' ? socket.once('close', r) : r()))
418
- : ending = new Promise(r => ended = r)
419
- )
420
- }
639
+ if (phase === Phase.Closed)
640
+ return Promise.resolve()
421
641
 
422
- function terminate() {
423
- terminated = true
424
- if (stream || query || initial || sent.length)
425
- error(Errors.connection('CONNECTION_DESTROYED', options))
642
+ const done = new Promise(resolve => endWaiters.push(resolve))
426
643
 
427
- clearImmediate(nextWriteTimer)
428
- if (socket) {
429
- socket.removeListener('data', data)
430
- socket.removeListener('connect', connected)
431
- socket.readyState === 'open' && socket.end(b().X().end())
644
+ if (phase === Phase.Closing || phase === Phase.Draining)
645
+ return done
646
+
647
+ if (phase === Phase.Ready) {
648
+ !held() && onend(connection)
649
+ idle() && !held()
650
+ ? closing(true)
651
+ : transition(Phase.Draining)
652
+ return done
432
653
  }
433
- ended && (ended(), ending = ended = null)
654
+
655
+ if (acquisition.owner.reserve)
656
+ enterClosed(Errors.connection('CONNECTION_ENDED', options))
657
+ else if (phase === Phase.Backoff && acquisition.attempts > 0)
658
+ enterClosed(acquisition.lastError)
659
+ else if (!endRequested)
660
+ (endRequested = true, onend(connection))
661
+
662
+ return done
434
663
  }
435
664
 
436
- async function closed(hadError) {
437
- incoming = Buffer.alloc(0)
438
- remaining = 0
439
- incomings = null
440
- clearImmediate(nextWriteTimer)
441
- socket.removeListener('data', data)
442
- socket.removeListener('connect', connected)
443
- idleTimer.cancel()
444
- lifeTimer.cancel()
445
- connectTimer.cancel()
665
+ function terminate() {
666
+ if (phase === Phase.Closed)
667
+ return
446
668
 
447
- socket.removeAllListeners()
448
- socket = null
669
+ const destroyed = Errors.connection('CONNECTION_DESTROYED', options, socket)
670
+ if (phase === Phase.Closing)
671
+ return socket.destroy()
449
672
 
450
- if (initial)
451
- return reconnect()
673
+ if (phase !== Phase.Ready && phase !== Phase.Draining)
674
+ return enterClosed(destroyed)
452
675
 
453
- !hadError && (query || sent.length) && error(Errors.connection('CONNECTION_CLOSED', options, socket))
454
- closedTime = performance.now()
455
- hadError && options.shared.retries++
456
- delay = (typeof backoff === 'function' ? backoff(options.shared.retries) : backoff) * 1000
457
- onclose(connection, Errors.connection('CONNECTION_CLOSED', options, socket))
676
+ const inflight = !idle()
677
+ resetSocketState(destroyed)
678
+ enterClosed(inflight ? destroyed : undefined)
458
679
  }
459
680
 
681
+ function release() {
682
+ phase === Phase.Ready
683
+ ? onopen(connection)
684
+ : phase === Phase.Draining && idle() && drained()
685
+ }
686
+
687
+ function closed(hadError) {
688
+ const err = Errors.connection('CONNECTION_CLOSED', options, socket)
689
+ if (starting())
690
+ return fail(errorResponse || err, true)
691
+
692
+ if (!inheritedBackoff && (phase !== Phase.Closing || hadError)) {
693
+ hadError && options.shared.retries++
694
+ inheritedBackoff = { at: performance.now(), delay: backoffMs() }
695
+ }
696
+ enterClosed(err)
697
+ }
698
+
699
+
460
700
  /* Handlers */
461
701
  function handle(xs, x = xs[0]) {
462
702
  (
@@ -495,6 +735,8 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
495
735
  let column
496
736
  let value
497
737
 
738
+ const valueFrom = !query.initialization && transform.value.from
739
+ const rowFrom = !query.initialization && transform.row.from
498
740
  const row = query.isRaw ? new Array(query.statement.columns.length) : {}
499
741
  for (let i = 0; i < query.statement.columns.length; i++) {
500
742
  column = query.statement.columns[i]
@@ -514,13 +756,13 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
514
756
  query.isRaw
515
757
  ? (row[i] = query.isRaw === true
516
758
  ? value
517
- : transform.value.from ? transform.value.from(value, column) : value)
518
- : (row[column.name] = transform.value.from ? transform.value.from(value, column) : value)
759
+ : valueFrom ? transform.value.from(value, column) : value)
760
+ : (row[column.name] = valueFrom ? transform.value.from(value, column) : value)
519
761
  }
520
762
 
521
763
  query.forEachFn
522
- ? query.forEachFn(transform.row.from ? transform.row.from(row) : row, result)
523
- : (result[rows++] = transform.row.from ? transform.row.from(row) : row)
764
+ ? query.forEachFn(rowFrom ? transform.row.from(row) : row, result)
765
+ : (result[rows++] = rowFrom ? transform.row.from(row) : row)
524
766
  }
525
767
 
526
768
  function ParameterStatus(x) {
@@ -535,12 +777,15 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
535
777
  function ReadyForQuery(x) {
536
778
  if (query) {
537
779
  if (errorResponse) {
780
+ if (query.initialization)
781
+ return enterClosed(errorResponse)
538
782
  query.retried
539
783
  ? errored(query.retried)
540
784
  : query.prepared && retryRoutines.has(errorResponse.routine)
541
785
  ? retry(query, errorResponse)
542
786
  : errored(errorResponse)
543
787
  } else {
788
+ query.initialization && query.initialization(results || result)
544
789
  query.resolve(results || result)
545
790
  }
546
791
  } else if (errorResponse) {
@@ -549,44 +794,46 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
549
794
 
550
795
  query = results = errorResponse = null
551
796
  result = new Result()
552
- connectTimer.cancel()
553
-
554
- if (initial) {
555
- if (target_session_attrs) {
556
- if (!backendParameters.in_hot_standby || !backendParameters.default_transaction_read_only)
557
- return fetchState()
558
- else if (tryNext(target_session_attrs, backendParameters))
559
- return terminate()
560
- }
561
797
 
562
- if (needsTypes) {
563
- initial.reserve && (initial = null)
564
- return fetchArrayTypes()
565
- }
798
+ if (phase === Phase.Authenticating || phase === Phase.Initializing)
799
+ return initialized()
566
800
 
567
- initial && !initial.reserve && execute(initial)
568
- options.shared.retries = retries = 0
569
- initial = null
801
+ if (phase === Phase.Closing)
570
802
  return
571
- }
572
803
 
573
804
  while (sent.length && (query = sent.shift()) && (query.active = true, query.cancelled))
574
805
  Connection(options).cancel(query.state, query.cancelled.resolve, query.cancelled.reject)
575
806
 
576
807
  if (query)
577
- return // Consider opening if able and sent.length < 50
808
+ return
578
809
 
579
810
  connection.reserved
580
- ? !connection.reserved.release && x[5] === 73 // I
581
- ? ending
582
- ? terminate()
811
+ ? !connection.reserved.release && x[5] === 73
812
+ ? phase === Phase.Draining
813
+ ? drained()
583
814
  : (connection.reserved = null, onopen(connection))
584
815
  : connection.reserved()
585
- : ending
586
- ? terminate()
816
+ : phase === Phase.Draining
817
+ ? drained()
587
818
  : onopen(connection)
588
819
  }
589
820
 
821
+ function initialized() {
822
+ if (target_session_attrs) {
823
+ if (!backendParameters.in_hot_standby || !backendParameters.default_transaction_read_only)
824
+ return fetchState()
825
+ if (tryNext(target_session_attrs, backendParameters)) {
826
+ acquisition.mismatch = true
827
+ return fail(Errors.connection('CONNECTION_CLOSED', options, socket), true)
828
+ }
829
+ }
830
+
831
+ if (needsTypes)
832
+ return fetchArrayTypes()
833
+
834
+ handoff()
835
+ }
836
+
590
837
  function CommandComplete(x) {
591
838
  rows = 0
592
839
 
@@ -654,7 +901,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
654
901
  const number = x.readUInt16BE(index + 4)
655
902
  const type = x.readUInt32BE(index + 6)
656
903
  query.statement.columns[i] = {
657
- name: transform.column.from
904
+ name: !query.initialization && transform.column.from
658
905
  ? transform.column.from(x.toString('utf8', start, index - 1))
659
906
  : x.toString('utf8', start, index - 1),
660
907
  parser: parsers[type],
@@ -670,8 +917,9 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
670
917
  return (query.resolve(query.statement), write(Sync))
671
918
  }
672
919
 
673
- async function Authentication(x, type = x.readUInt32BE(5)) {
674
- (
920
+ function Authentication(x, type = x.readUInt32BE(5)) {
921
+ const attempt = generation
922
+ Promise.resolve((
675
923
  type === 3 ? AuthenticationCleartextPassword :
676
924
  type === 5 ? AuthenticationMD5Password :
677
925
  type === 10 ? SASL :
@@ -679,18 +927,20 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
679
927
  type === 12 ? SASLFinal :
680
928
  type !== 0 ? UnknownAuth :
681
929
  noop
682
- )(x, type)
930
+ )(x, type, attempt)).catch(err => attempt === generation && enterClosed(err))
683
931
  }
684
932
 
685
933
  /* c8 ignore next 5 */
686
- async function AuthenticationCleartextPassword() {
934
+ async function AuthenticationCleartextPassword(x, type, attempt) {
687
935
  const payload = await Pass()
936
+ if (attempt !== generation)
937
+ return
688
938
  write(
689
939
  b().p().str(payload).z(1).end()
690
940
  )
691
941
  }
692
942
 
693
- async function AuthenticationMD5Password(x) {
943
+ async function AuthenticationMD5Password(x, type, attempt) {
694
944
  const payload = 'md5' + (
695
945
  await md5(
696
946
  Buffer.concat([
@@ -699,19 +949,25 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
699
949
  ])
700
950
  )
701
951
  )
952
+ if (attempt !== generation)
953
+ return
702
954
  write(
703
955
  b().p().str(payload).z(1).end()
704
956
  )
705
957
  }
706
958
 
707
- async function SASL() {
708
- nonce = (await crypto.randomBytes(18)).toString('base64')
959
+ async function SASL(x, type, attempt) {
960
+ const nextNonce = (await crypto.randomBytes(18)).toString('base64')
961
+ if (attempt !== generation)
962
+ return
963
+ nonce = nextNonce
709
964
  b().p().str('SCRAM-SHA-256' + b.N)
710
965
  const i = b.i
711
966
  write(b.inc(4).str('n,,n=*,r=' + nonce).i32(b.i - i - 4, i).end())
712
967
  }
713
968
 
714
- async function SASLContinue(x) {
969
+ async function SASLContinue(x, type, attempt) {
970
+ const clientNonce = nonce
715
971
  const res = x.toString('utf8', 9).split(',').reduce((acc, x) => (acc[x[0]] = x.slice(2), acc), {})
716
972
 
717
973
  const saltedPassword = await crypto.pbkdf2Sync(
@@ -723,16 +979,19 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
723
979
 
724
980
  const clientKey = await hmac(saltedPassword, 'Client Key')
725
981
 
726
- const auth = 'n=*,r=' + nonce + ','
982
+ const auth = 'n=*,r=' + clientNonce + ','
727
983
  + 'r=' + res.r + ',s=' + res.s + ',i=' + res.i
728
984
  + ',c=biws,r=' + res.r
729
985
 
730
- serverSignature = (await hmac(await hmac(saltedPassword, 'Server Key'), auth)).toString('base64')
986
+ const signature = (await hmac(await hmac(saltedPassword, 'Server Key'), auth)).toString('base64')
731
987
 
732
988
  const payload = 'c=biws,r=' + res.r + ',p=' + xor(
733
989
  clientKey, Buffer.from(await hmac(await sha256(clientKey), auth))
734
990
  ).toString('base64')
735
991
 
992
+ if (attempt !== generation)
993
+ return
994
+ serverSignature = signature
736
995
  write(
737
996
  b().p().str(payload).end()
738
997
  )
@@ -742,8 +1001,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
742
1001
  if (x.toString('utf8', 9).split(b.N, 1)[0].slice(2) === serverSignature)
743
1002
  return
744
1003
  /* c8 ignore next 5 */
745
- errored(Errors.generic('SASL_SIGNATURE_MISMATCH', 'The server did not return the correct signature'))
746
- socket.destroy()
1004
+ enterClosed(Errors.generic('SASL_SIGNATURE_MISMATCH', 'The server did not return the correct signature'))
747
1005
  }
748
1006
 
749
1007
  function Pass() {
@@ -765,26 +1023,16 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
765
1023
  backend.secret = x.readUInt32BE(9)
766
1024
  }
767
1025
 
768
- async function fetchArrayTypes() {
1026
+ function fetchArrayTypes() {
769
1027
  needsTypes = false
770
- const types = await new Query([`
1028
+ initialize(`
771
1029
  select b.oid, b.typarray
772
1030
  from pg_catalog.pg_type a
773
1031
  left join pg_catalog.pg_type b on b.oid = a.typelem
774
1032
  where a.typcategory = 'A'
775
1033
  group by b.oid, b.typarray
776
1034
  order by b.oid
777
- `], [], execute)
778
- types.forEach(({ oid, typarray }) => addArrayType(oid, typarray))
779
- }
780
-
781
- function addArrayType(oid, typarray) {
782
- if (!!options.parsers[typarray] && !!options.serializers[typarray]) return
783
- const parser = options.parsers[oid]
784
- options.shared.typeArrayMap[oid] = typarray
785
- options.parsers[typarray] = (xs) => arrayParser(xs, parser, typarray)
786
- options.parsers[typarray].array = true
787
- options.serializers[typarray] = (xs) => arraySerializer(xs, options.serializers[oid], options, typarray)
1035
+ `, types => types.forEach(({ oid, typarray }) => addArrayType(options, oid, typarray)))
788
1036
  }
789
1037
 
790
1038
  function tryNext(x, xs) {
@@ -793,20 +1041,28 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
793
1041
  (x === 'read-only' && xs.default_transaction_read_only === 'off') ||
794
1042
  (x === 'primary' && xs.in_hot_standby === 'on') ||
795
1043
  (x === 'standby' && xs.in_hot_standby === 'off') ||
796
- (x === 'prefer-standby' && xs.in_hot_standby === 'off' && options.host[retries])
1044
+ (x === 'prefer-standby' && xs.in_hot_standby === 'off' && host.length > 1 && !acquisition.mismatch)
797
1045
  )
798
1046
  }
799
1047
 
800
1048
  function fetchState() {
801
- const query = new Query([`
1049
+ initialize(`
802
1050
  show transaction_read_only;
803
1051
  select pg_catalog.pg_is_in_recovery()
804
- `], [], execute, null, { simple: true })
805
- query.resolve = ([[a], [b]]) => {
1052
+ `, ([[a], [b]]) => {
806
1053
  backendParameters.default_transaction_read_only = a.transaction_read_only
807
1054
  backendParameters.in_hot_standby = b.pg_is_in_recovery ? 'on' : 'off'
808
- }
809
- query.execute()
1055
+ }, true)
1056
+ }
1057
+
1058
+ function initialize(string, resolve, simple = false) {
1059
+ transition(Phase.Initializing)
1060
+ const attempt = generation
1061
+ const q = new Query([string], [], q => attempt === generation
1062
+ ? execute(q)
1063
+ : queryError(q, Errors.connection('CONNECTION_CLOSED', options)), null, { simple })
1064
+ q.initialization = resolve
1065
+ q.catch(noop)
810
1066
  }
811
1067
 
812
1068
  function ErrorResponse(x) {
@@ -814,7 +1070,10 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
814
1070
  (query.cursorFn || query.describeFirst) && write(Sync)
815
1071
  errorResponse = Errors.postgres(parseError(x))
816
1072
  } else {
817
- errored(Errors.postgres(parseError(x)))
1073
+ const err = Errors.postgres(parseError(x))
1074
+ phase === Phase.Ready || phase === Phase.Draining
1075
+ ? socketFailed(err)
1076
+ : phase === Phase.Authenticating && fail(err)
818
1077
  }
819
1078
  }
820
1079
 
@@ -837,15 +1096,19 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
837
1096
  }
838
1097
 
839
1098
  async function PortalSuspended() {
1099
+ const current = query
1100
+ const source = socket
840
1101
  try {
841
- const x = await Promise.resolve(query.cursorFn(result))
1102
+ const x = await Promise.resolve(current.cursorFn(result))
1103
+ if (query !== current || socket !== source)
1104
+ return
842
1105
  rows = 0
843
1106
  x === CLOSE
844
- ? write(Close(query.portal))
845
- : (result = new Result(), write(Execute('', query.cursorRows)))
1107
+ ? write(Close(current.portal))
1108
+ : (result = new Result(), write(Execute('', current.cursorRows)))
846
1109
  } catch (err) {
847
- write(Sync)
848
- query.reject(err)
1110
+ query === current && socket === source && write(Sync)
1111
+ current.reject(err)
849
1112
  }
850
1113
  }
851
1114
 
@@ -862,7 +1125,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
862
1125
  },
863
1126
  destroy(error, callback) {
864
1127
  callback(error)
865
- socket.write(b().f().str(error + b.N).end())
1128
+ socket && socket.readyState === 'open' && socket.write(b().f().str(error + b.N).end())
866
1129
  stream = null
867
1130
  },
868
1131
  final(callback) {
@@ -892,7 +1155,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
892
1155
  },
893
1156
  destroy(error, callback) {
894
1157
  callback(error)
895
- socket.write(b().f().str(error + b.N).end())
1158
+ socket && socket.readyState === 'open' && socket.write(b().f().str(error + b.N).end())
896
1159
  stream = null
897
1160
  },
898
1161
  final(callback) {
@@ -994,7 +1257,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
994
1257
  }
995
1258
 
996
1259
  function StartupMessage() {
997
- return cancelMessage || b().inc(4).i16(3).z(2).str(
1260
+ return b().inc(4).i16(3).z(2).str(
998
1261
  Object.entries(Object.assign({
999
1262
  user,
1000
1263
  database,
@@ -1049,14 +1312,14 @@ function timer(fn, seconds) {
1049
1312
  cancel() {
1050
1313
  timer && (clearTimeout(timer), timer = null)
1051
1314
  },
1052
- start() {
1315
+ start(...args) {
1053
1316
  timer && clearTimeout(timer)
1054
- timer = setTimeout(done, seconds * 1000, arguments)
1317
+ timer = setTimeout(() => done(args), seconds * 1000)
1055
1318
  }
1056
1319
  }
1057
1320
 
1058
1321
  function done(args) {
1059
- fn.apply(null, args)
1060
1322
  timer = null
1323
+ fn.apply(null, args)
1061
1324
  }
1062
1325
  }