@steadybrew/postgresjs 3.4.9 → 4.0.0-rc1

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