@steadybrew/postgresjs 0.0.0-stage → 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.
@@ -0,0 +1,1327 @@
1
+ import { Buffer } from 'node:buffer'
2
+ import { setImmediate, clearImmediate } from '../polyfills.js'
3
+ import { net } from '../polyfills.js'
4
+ import { tls } from '../polyfills.js'
5
+ import { crypto } from '../polyfills.js'
6
+ import Stream from 'node:stream'
7
+ import { performance } from '../polyfills.js'
8
+
9
+ import { stringify, handleValue, addArrayType } from './types.js'
10
+ import { Errors } from './errors.js'
11
+ import Result from './result.js'
12
+ import Queue from './queue.js'
13
+ import { Query, CLOSE } from './query.js'
14
+ import b from './bytes.js'
15
+
16
+ export default Connection
17
+
18
+ let uid = 1
19
+
20
+ const Sync = b().S().end()
21
+ , Flush = b().H().end()
22
+ , SSLRequest = b().i32(8).i32(80877103).end(8)
23
+ , ExecuteUnnamed = Buffer.concat([b().E().str(b.N).i32(0).end(), Sync])
24
+ , DescribeUnnamed = b().D().str('S').str(b.N).end()
25
+ , noop = () => { /* noop */ }
26
+
27
+ const Phase = { Closed: 0, Backoff: 1, Opening: 2, Negotiating: 3, Authenticating: 4, Initializing: 5, Ready: 6, Draining: 7, Closing: 8 }
28
+
29
+ const retryRoutines = new Set([
30
+ 'FetchPreparedStatement',
31
+ 'RevalidateCachedQuery',
32
+ 'transformAssignedExpr'
33
+ ])
34
+
35
+ const errorFields = {
36
+ 83 : 'severity_local', // S
37
+ 86 : 'severity', // V
38
+ 67 : 'code', // C
39
+ 77 : 'message', // M
40
+ 68 : 'detail', // D
41
+ 72 : 'hint', // H
42
+ 80 : 'position', // P
43
+ 112 : 'internal_position', // p
44
+ 113 : 'internal_query', // q
45
+ 87 : 'where', // W
46
+ 115 : 'schema_name', // s
47
+ 116 : 'table_name', // t
48
+ 99 : 'column_name', // c
49
+ 100 : 'data type_name', // d
50
+ 110 : 'constraint_name', // n
51
+ 70 : 'file', // F
52
+ 76 : 'line', // L
53
+ 82 : 'routine' // R
54
+ }
55
+
56
+ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose = noop, ondrain = noop } = {}) {
57
+ const {
58
+ sslnegotiation,
59
+ ssl,
60
+ max,
61
+ user,
62
+ host,
63
+ port,
64
+ database,
65
+ parsers,
66
+ transform,
67
+ onnotice,
68
+ onnotify,
69
+ onparameter,
70
+ max_pipeline,
71
+ keep_alive,
72
+ backoff,
73
+ target_session_attrs
74
+ } = options
75
+
76
+ const sent = Queue()
77
+ , id = uid++
78
+ , backend = { pid: null, secret: null }
79
+ , idleTimer = timer(end, options.idle_timeout)
80
+ , lifeTimer = timer(end, options.max_lifetime)
81
+
82
+ let socket = null
83
+ , phase = Phase.Closed
84
+ , generation = 0
85
+ , acquisition = null
86
+ , endRequested = false
87
+ , endWaiters = []
88
+ , inheritedBackoff = null
89
+ , backoffTimer = null
90
+ , connectTimer = null
91
+ , errorResponse = null
92
+ , result = new Result()
93
+ , incoming = Buffer.alloc(0)
94
+ , needsTypes = options.fetch_types
95
+ , backendParameters = {}
96
+ , statements = {}
97
+ , statementId = Math.random().toString(36).slice(2)
98
+ , statementCount = 1
99
+ , remaining = 0
100
+ , hostIndex = 0
101
+ , length = 0
102
+ , rows = 0
103
+ , serverSignature = null
104
+ , nextWriteTimer = null
105
+ , incomings = null
106
+ , results = null
107
+ , stream = null
108
+ , chunk = null
109
+ , nonce = null
110
+ , query = null
111
+ , final = null
112
+
113
+ const connection = {
114
+ queue: queues.closed,
115
+ idleTimer,
116
+ connect: acquire,
117
+ terminate,
118
+ execute,
119
+ cancel,
120
+ release,
121
+ end,
122
+ count: 0,
123
+ id
124
+ }
125
+
126
+ queues.closed && queues.closed.push(connection)
127
+
128
+ return connection
129
+
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)
275
+ try {
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())
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
338
+ }
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)
392
+ }
393
+
394
+ async function cancel({ pid, secret }, resolve, reject) {
395
+ let timeout = null
396
+ try {
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]
436
+ } catch (error) {
437
+ reject(error)
438
+ }
439
+ }
440
+
441
+ function execute(q) {
442
+ if (phase !== Phase.Ready && phase !== Phase.Draining && !(phase === Phase.Initializing && q.initialization))
443
+ return queryError(q, Errors.connection('CONNECTION_CLOSED', options))
444
+
445
+ if (stream)
446
+ return queryError(q, Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy'))
447
+
448
+ if (q.cancelled)
449
+ return
450
+
451
+ try {
452
+ q.state = backend
453
+ query
454
+ ? sent.push(q)
455
+ : (query = q, query.active = true)
456
+
457
+ build(q)
458
+ return write(toBuffer(q))
459
+ && !q.describeFirst
460
+ && !q.cursorFn
461
+ && sent.length < max_pipeline
462
+ } catch (error) {
463
+ sent.length === 0 && write(Sync)
464
+ errored(error)
465
+ return true
466
+ }
467
+ }
468
+
469
+ function toBuffer(q) {
470
+ if (q.parameters.length >= 65534)
471
+ throw Errors.generic('MAX_PARAMETERS_EXCEEDED', 'Max number of parameters (65534) exceeded')
472
+
473
+ return q.options.simple
474
+ ? b().Q().str(q.statement.string + b.N).end()
475
+ : q.describeFirst
476
+ ? Buffer.concat([describe(q), Flush])
477
+ : q.prepare
478
+ ? q.prepared
479
+ ? prepared(q)
480
+ : Buffer.concat([describe(q), prepared(q)])
481
+ : unnamed(q)
482
+ }
483
+
484
+ function describe(q) {
485
+ return Buffer.concat([
486
+ Parse(q.statement.string, q.parameters, q.statement.types, q.statement.name),
487
+ Describe('S', q.statement.name)
488
+ ])
489
+ }
490
+
491
+ function prepared(q) {
492
+ return Buffer.concat([
493
+ Bind(q.parameters, q.statement.types, q.statement.name, q.cursorName),
494
+ q.cursorFn
495
+ ? Execute('', q.cursorRows)
496
+ : ExecuteUnnamed
497
+ ])
498
+ }
499
+
500
+ function unnamed(q) {
501
+ return Buffer.concat([
502
+ Parse(q.statement.string, q.parameters, q.statement.types),
503
+ DescribeUnnamed,
504
+ prepared(q)
505
+ ])
506
+ }
507
+
508
+ function build(q) {
509
+ const parameters = []
510
+ , types = []
511
+
512
+ const string = stringify(q, q.strings[0], q.args[0], parameters, types, options)
513
+
514
+ !q.tagged && q.args.forEach(x => handleValue(x, parameters, types, options))
515
+
516
+ q.prepare = options.prepare && ('prepare' in q.options ? q.options.prepare : true)
517
+ q.string = string
518
+ q.signature = q.prepare && types + string
519
+ q.onlyDescribe && (delete statements[q.signature])
520
+ q.parameters = q.parameters || parameters
521
+ q.prepared = q.prepare && q.signature in statements
522
+ q.describeFirst = q.onlyDescribe || (parameters.length && !q.prepared)
523
+ q.statement = q.prepared
524
+ ? statements[q.signature]
525
+ : { string, types, name: q.prepare ? statementId + statementCount++ : '' }
526
+
527
+ typeof options.debug === 'function' && options.debug(id, string, parameters, types)
528
+ }
529
+
530
+ function write(x, fn) {
531
+ chunk = chunk ? Buffer.concat([chunk, x]) : Buffer.from(x)
532
+ if (fn || chunk.length >= 1024)
533
+ return nextWrite(fn)
534
+ nextWriteTimer === null && (nextWriteTimer = setImmediate(nextWrite))
535
+ return true
536
+ }
537
+
538
+ function nextWrite(fn) {
539
+ const x = socket ? socket.write(chunk, fn) : false
540
+ nextWriteTimer !== null && clearImmediate(nextWriteTimer)
541
+ chunk = nextWriteTimer = null
542
+ return x
543
+ }
544
+
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
556
+ }
557
+
558
+ /* c8 ignore next 3 */
559
+ function drain() {
560
+ phase === Phase.Ready && !query && !held() && onopen(connection)
561
+ }
562
+
563
+ function data(x) {
564
+ if (incomings) {
565
+ incomings.push(x)
566
+ remaining -= x.length
567
+ if (remaining > 0)
568
+ return
569
+ }
570
+
571
+ incoming = incomings
572
+ ? Buffer.concat(incomings, length - remaining)
573
+ : incoming.length === 0
574
+ ? x
575
+ : Buffer.concat([incoming, x], incoming.length + x.length)
576
+
577
+ const source = socket
578
+ while (incoming.length > 4) {
579
+ length = incoming.readUInt32BE(1)
580
+ if (length >= incoming.length) {
581
+ remaining = length - incoming.length
582
+ incomings = [incoming]
583
+ break
584
+ }
585
+
586
+ try {
587
+ handle(incoming.subarray(0, length + 1))
588
+ } catch (e) {
589
+ query && (query.cursorFn || query.describeFirst) && write(Sync)
590
+ starting() ? fail(e) : errored(e)
591
+ }
592
+ if (socket !== source)
593
+ return
594
+ incoming = incoming.subarray(length + 1)
595
+ remaining = 0
596
+ incomings = null
597
+ }
598
+ }
599
+
600
+ function error(err) {
601
+ if (starting())
602
+ return fail(err)
603
+
604
+ if (phase === Phase.Ready || phase === Phase.Draining)
605
+ socketFailed(err)
606
+ }
607
+
608
+ function socketFailed(err) {
609
+ errored(err)
610
+ while (sent.length)
611
+ queryError(sent.shift(), err)
612
+ query = null
613
+ options.shared.retries++
614
+ inheritedBackoff = { at: performance.now(), delay: backoffMs() }
615
+ closing(false)
616
+ }
617
+
618
+ function errored(err) {
619
+ stream && (stream.destroy(err), stream = null)
620
+ query && queryError(query, err)
621
+ }
622
+
623
+ function queryError(query, err) {
624
+ if (query.reserve)
625
+ return query.reject(err)
626
+
627
+ if (!err || typeof err !== 'object')
628
+ err = new Error(err)
629
+
630
+ 'query' in err || 'parameters' in err || Object.defineProperties(err, {
631
+ stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug },
632
+ query: { value: query.string, enumerable: options.debug },
633
+ parameters: { value: query.parameters, enumerable: options.debug },
634
+ args: { value: query.args, enumerable: options.debug },
635
+ types: { value: query.statement && query.statement.types, enumerable: options.debug }
636
+ })
637
+ query.reject(err)
638
+ }
639
+
640
+ function end() {
641
+ if (phase === Phase.Closed)
642
+ return Promise.resolve()
643
+
644
+ const done = new Promise(resolve => endWaiters.push(resolve))
645
+
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
655
+ }
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
665
+ }
666
+
667
+ function terminate() {
668
+ if (phase === Phase.Closed)
669
+ return
670
+
671
+ const destroyed = Errors.connection('CONNECTION_DESTROYED', options, socket)
672
+ if (phase === Phase.Closing)
673
+ return socket.destroy()
674
+
675
+ if (phase !== Phase.Ready && phase !== Phase.Draining)
676
+ return enterClosed(destroyed)
677
+
678
+ const inflight = !idle()
679
+ resetSocketState(destroyed)
680
+ enterClosed(inflight ? destroyed : undefined)
681
+ }
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
+
702
+ /* Handlers */
703
+ function handle(xs, x = xs[0]) {
704
+ (
705
+ x === 68 ? DataRow : // D
706
+ x === 100 ? CopyData : // d
707
+ x === 65 ? NotificationResponse : // A
708
+ x === 83 ? ParameterStatus : // S
709
+ x === 90 ? ReadyForQuery : // Z
710
+ x === 67 ? CommandComplete : // C
711
+ x === 50 ? BindComplete : // 2
712
+ x === 49 ? ParseComplete : // 1
713
+ x === 116 ? ParameterDescription : // t
714
+ x === 84 ? RowDescription : // T
715
+ x === 82 ? Authentication : // R
716
+ x === 110 ? NoData : // n
717
+ x === 75 ? BackendKeyData : // K
718
+ x === 69 ? ErrorResponse : // E
719
+ x === 115 ? PortalSuspended : // s
720
+ x === 51 ? CloseComplete : // 3
721
+ x === 71 ? CopyInResponse : // G
722
+ x === 78 ? NoticeResponse : // N
723
+ x === 72 ? CopyOutResponse : // H
724
+ x === 99 ? CopyDone : // c
725
+ x === 73 ? EmptyQueryResponse : // I
726
+ x === 86 ? FunctionCallResponse : // V
727
+ x === 118 ? NegotiateProtocolVersion : // v
728
+ x === 87 ? CopyBothResponse : // W
729
+ /* c8 ignore next */
730
+ UnknownMessage
731
+ )(xs)
732
+ }
733
+
734
+ function DataRow(x) {
735
+ let index = 7
736
+ let length
737
+ let column
738
+ let value
739
+
740
+ const valueFrom = !query.initialization && transform.value.from
741
+ const rowFrom = !query.initialization && transform.row.from
742
+ const row = query.isRaw ? new Array(query.statement.columns.length) : {}
743
+ for (let i = 0; i < query.statement.columns.length; i++) {
744
+ column = query.statement.columns[i]
745
+ length = x.readInt32BE(index)
746
+ index += 4
747
+
748
+ value = length === -1
749
+ ? null
750
+ : query.isRaw === true
751
+ ? x.subarray(index, index += length)
752
+ : column.parser === undefined
753
+ ? x.toString('utf8', index, index += length)
754
+ : column.parser.array === true
755
+ ? column.parser(x.toString('utf8', index + 1, index += length))
756
+ : column.parser(x.toString('utf8', index, index += length))
757
+
758
+ query.isRaw
759
+ ? (row[i] = query.isRaw === true
760
+ ? value
761
+ : valueFrom ? transform.value.from(value, column) : value)
762
+ : (row[column.name] = valueFrom ? transform.value.from(value, column) : value)
763
+ }
764
+
765
+ query.forEachFn
766
+ ? query.forEachFn(rowFrom ? transform.row.from(row) : row, result)
767
+ : (result[rows++] = rowFrom ? transform.row.from(row) : row)
768
+ }
769
+
770
+ function ParameterStatus(x) {
771
+ const [k, v] = x.toString('utf8', 5, x.length - 1).split(b.N)
772
+ backendParameters[k] = v
773
+ if (options.parameters[k] !== v) {
774
+ options.parameters[k] = v
775
+ onparameter && onparameter(k, v)
776
+ }
777
+ }
778
+
779
+ function ReadyForQuery(x) {
780
+ if (query) {
781
+ if (errorResponse) {
782
+ if (query.initialization)
783
+ return enterClosed(errorResponse)
784
+ query.retried
785
+ ? errored(query.retried)
786
+ : query.prepared && retryRoutines.has(errorResponse.routine)
787
+ ? retry(query, errorResponse)
788
+ : errored(errorResponse)
789
+ } else {
790
+ query.initialization && query.initialization(results || result)
791
+ query.resolve(results || result)
792
+ }
793
+ } else if (errorResponse) {
794
+ errored(errorResponse)
795
+ }
796
+
797
+ query = results = errorResponse = null
798
+ result = new Result()
799
+
800
+ if (phase === Phase.Authenticating || phase === Phase.Initializing)
801
+ return initialized()
802
+
803
+ if (phase === Phase.Closing)
804
+ return
805
+
806
+ while (sent.length && (query = sent.shift()) && (query.active = true, query.cancelled))
807
+ Connection(options).cancel(query.state, query.cancelled.resolve, query.cancelled.reject)
808
+
809
+ if (query)
810
+ return
811
+
812
+ connection.reserved
813
+ ? !connection.reserved.release && x[5] === 73
814
+ ? phase === Phase.Draining
815
+ ? drained()
816
+ : (connection.reserved = null, onopen(connection))
817
+ : connection.reserved()
818
+ : phase === Phase.Draining
819
+ ? drained()
820
+ : onopen(connection)
821
+ }
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
+
839
+ function CommandComplete(x) {
840
+ rows = 0
841
+
842
+ for (let i = x.length - 1; i > 0; i--) {
843
+ if (x[i] === 32 && x[i + 1] < 58 && result.count === null)
844
+ result.count = +x.toString('utf8', i + 1, x.length - 1)
845
+ if (x[i - 1] >= 65) {
846
+ result.command = x.toString('utf8', 5, i)
847
+ result.state = backend
848
+ break
849
+ }
850
+ }
851
+
852
+ final && (final(), final = null)
853
+
854
+ if (result.command === 'BEGIN' && max !== 1 && !connection.reserved)
855
+ return errored(Errors.generic('UNSAFE_TRANSACTION', 'Only use sql.begin, sql.reserved or max: 1'))
856
+
857
+ if (query.options.simple)
858
+ return BindComplete()
859
+
860
+ if (query.cursorFn) {
861
+ result.count && query.cursorFn(result)
862
+ write(Sync)
863
+ }
864
+ }
865
+
866
+ function ParseComplete() {
867
+ query.parsing = false
868
+ }
869
+
870
+ function BindComplete() {
871
+ !result.statement && (result.statement = query.statement)
872
+ result.columns = query.statement.columns
873
+ }
874
+
875
+ function ParameterDescription(x) {
876
+ const length = x.readUInt16BE(5)
877
+
878
+ for (let i = 0; i < length; ++i)
879
+ !query.statement.types[i] && (query.statement.types[i] = x.readUInt32BE(7 + i * 4))
880
+
881
+ query.prepare && (statements[query.signature] = query.statement)
882
+ query.describeFirst && !query.onlyDescribe && (write(prepared(query)), query.describeFirst = false)
883
+ }
884
+
885
+ function RowDescription(x) {
886
+ if (result.command) {
887
+ results = results || [result]
888
+ results.push(result = new Result())
889
+ result.count = null
890
+ query.statement.columns = null
891
+ }
892
+
893
+ const length = x.readUInt16BE(5)
894
+ let index = 7
895
+ let start
896
+
897
+ query.statement.columns = Array(length)
898
+
899
+ for (let i = 0; i < length; ++i) {
900
+ start = index
901
+ while (x[index++] !== 0);
902
+ const table = x.readUInt32BE(index)
903
+ const number = x.readUInt16BE(index + 4)
904
+ const type = x.readUInt32BE(index + 6)
905
+ query.statement.columns[i] = {
906
+ name: !query.initialization && transform.column.from
907
+ ? transform.column.from(x.toString('utf8', start, index - 1))
908
+ : x.toString('utf8', start, index - 1),
909
+ parser: parsers[type],
910
+ table,
911
+ number,
912
+ type
913
+ }
914
+ index += 18
915
+ }
916
+
917
+ result.statement = query.statement
918
+ if (query.onlyDescribe)
919
+ return (query.resolve(query.statement), write(Sync))
920
+ }
921
+
922
+ function Authentication(x, type = x.readUInt32BE(5)) {
923
+ const attempt = generation
924
+ Promise.resolve((
925
+ type === 3 ? AuthenticationCleartextPassword :
926
+ type === 5 ? AuthenticationMD5Password :
927
+ type === 10 ? SASL :
928
+ type === 11 ? SASLContinue :
929
+ type === 12 ? SASLFinal :
930
+ type !== 0 ? UnknownAuth :
931
+ noop
932
+ )(x, type, attempt)).catch(err => attempt === generation && enterClosed(err))
933
+ }
934
+
935
+ /* c8 ignore next 5 */
936
+ async function AuthenticationCleartextPassword(x, type, attempt) {
937
+ const payload = await Pass()
938
+ if (attempt !== generation)
939
+ return
940
+ write(
941
+ b().p().str(payload).z(1).end()
942
+ )
943
+ }
944
+
945
+ async function AuthenticationMD5Password(x, type, attempt) {
946
+ const payload = 'md5' + (
947
+ await md5(
948
+ Buffer.concat([
949
+ Buffer.from(await md5((await Pass()) + user)),
950
+ x.subarray(9)
951
+ ])
952
+ )
953
+ )
954
+ if (attempt !== generation)
955
+ return
956
+ write(
957
+ b().p().str(payload).z(1).end()
958
+ )
959
+ }
960
+
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
966
+ b().p().str('SCRAM-SHA-256' + b.N)
967
+ const i = b.i
968
+ write(b.inc(4).str('n,,n=*,r=' + nonce).i32(b.i - i - 4, i).end())
969
+ }
970
+
971
+ async function SASLContinue(x, type, attempt) {
972
+ const clientNonce = nonce
973
+ const res = x.toString('utf8', 9).split(',').reduce((acc, x) => (acc[x[0]] = x.slice(2), acc), {})
974
+
975
+ const saltedPassword = await crypto.pbkdf2Sync(
976
+ await Pass(),
977
+ Buffer.from(res.s, 'base64'),
978
+ parseInt(res.i), 32,
979
+ 'sha256'
980
+ )
981
+
982
+ const clientKey = await hmac(saltedPassword, 'Client Key')
983
+
984
+ const auth = 'n=*,r=' + clientNonce + ','
985
+ + 'r=' + res.r + ',s=' + res.s + ',i=' + res.i
986
+ + ',c=biws,r=' + res.r
987
+
988
+ const signature = (await hmac(await hmac(saltedPassword, 'Server Key'), auth)).toString('base64')
989
+
990
+ const payload = 'c=biws,r=' + res.r + ',p=' + xor(
991
+ clientKey, Buffer.from(await hmac(await sha256(clientKey), auth))
992
+ ).toString('base64')
993
+
994
+ if (attempt !== generation)
995
+ return
996
+ serverSignature = signature
997
+ write(
998
+ b().p().str(payload).end()
999
+ )
1000
+ }
1001
+
1002
+ function SASLFinal(x) {
1003
+ if (x.toString('utf8', 9).split(b.N, 1)[0].slice(2) === serverSignature)
1004
+ return
1005
+ /* c8 ignore next 5 */
1006
+ enterClosed(Errors.generic('SASL_SIGNATURE_MISMATCH', 'The server did not return the correct signature'))
1007
+ }
1008
+
1009
+ function Pass() {
1010
+ return Promise.resolve(typeof options.pass === 'function'
1011
+ ? options.pass()
1012
+ : options.pass
1013
+ )
1014
+ }
1015
+
1016
+ function NoData() {
1017
+ result.statement = query.statement
1018
+ result.statement.columns = []
1019
+ if (query.onlyDescribe)
1020
+ return (query.resolve(query.statement), write(Sync))
1021
+ }
1022
+
1023
+ function BackendKeyData(x) {
1024
+ backend.pid = x.readUInt32BE(5)
1025
+ backend.secret = x.readUInt32BE(9)
1026
+ }
1027
+
1028
+ function fetchArrayTypes() {
1029
+ needsTypes = false
1030
+ initialize(`
1031
+ select b.oid, b.typarray
1032
+ from pg_catalog.pg_type a
1033
+ left join pg_catalog.pg_type b on b.oid = a.typelem
1034
+ where a.typcategory = 'A'
1035
+ group by b.oid, b.typarray
1036
+ order by b.oid
1037
+ `, types => types.forEach(({ oid, typarray }) => addArrayType(options, oid, typarray)))
1038
+ }
1039
+
1040
+ function tryNext(x, xs) {
1041
+ return (
1042
+ (x === 'read-write' && xs.default_transaction_read_only === 'on') ||
1043
+ (x === 'read-only' && xs.default_transaction_read_only === 'off') ||
1044
+ (x === 'primary' && xs.in_hot_standby === 'on') ||
1045
+ (x === 'standby' && xs.in_hot_standby === 'off') ||
1046
+ (x === 'prefer-standby' && xs.in_hot_standby === 'off' && host.length > 1 && !acquisition.mismatch)
1047
+ )
1048
+ }
1049
+
1050
+ function fetchState() {
1051
+ initialize(`
1052
+ show transaction_read_only;
1053
+ select pg_catalog.pg_is_in_recovery()
1054
+ `, ([[a], [b]]) => {
1055
+ backendParameters.default_transaction_read_only = a.transaction_read_only
1056
+ backendParameters.in_hot_standby = b.pg_is_in_recovery ? 'on' : 'off'
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)
1068
+ }
1069
+
1070
+ function ErrorResponse(x) {
1071
+ if (query) {
1072
+ (query.cursorFn || query.describeFirst) && write(Sync)
1073
+ errorResponse = Errors.postgres(parseError(x))
1074
+ } else {
1075
+ const err = Errors.postgres(parseError(x))
1076
+ phase === Phase.Ready || phase === Phase.Draining
1077
+ ? socketFailed(err)
1078
+ : phase === Phase.Authenticating && fail(err)
1079
+ }
1080
+ }
1081
+
1082
+ function retry(q, error) {
1083
+ delete statements[q.signature]
1084
+ q.retried = error
1085
+ execute(q)
1086
+ }
1087
+
1088
+ function NotificationResponse(x) {
1089
+ if (!onnotify)
1090
+ return
1091
+
1092
+ let index = 9
1093
+ while (x[index++] !== 0);
1094
+ onnotify(
1095
+ x.toString('utf8', 9, index - 1),
1096
+ x.toString('utf8', index, x.length - 1)
1097
+ )
1098
+ }
1099
+
1100
+ async function PortalSuspended() {
1101
+ const current = query
1102
+ const source = socket
1103
+ try {
1104
+ const x = await Promise.resolve(current.cursorFn(result))
1105
+ if (query !== current || socket !== source)
1106
+ return
1107
+ rows = 0
1108
+ x === CLOSE
1109
+ ? write(Close(current.portal))
1110
+ : (result = new Result(), write(Execute('', current.cursorRows)))
1111
+ } catch (err) {
1112
+ query === current && socket === source && write(Sync)
1113
+ current.reject(err)
1114
+ }
1115
+ }
1116
+
1117
+ function CloseComplete() {
1118
+ result.count && query.cursorFn(result)
1119
+ query.resolve(result)
1120
+ }
1121
+
1122
+ function CopyInResponse() {
1123
+ stream = new Stream.Writable({
1124
+ autoDestroy: true,
1125
+ write(chunk, encoding, callback) {
1126
+ socket.write(b().d().raw(chunk).end(), callback)
1127
+ },
1128
+ destroy(error, callback) {
1129
+ callback(error)
1130
+ socket && socket.readyState === 'open' && socket.write(b().f().str(error + b.N).end())
1131
+ stream = null
1132
+ },
1133
+ final(callback) {
1134
+ socket.write(b().c().end())
1135
+ final = callback
1136
+ stream = null
1137
+ }
1138
+ })
1139
+ query.resolve(stream)
1140
+ }
1141
+
1142
+ function CopyOutResponse() {
1143
+ stream = new Stream.Readable({
1144
+ read() { socket.resume() }
1145
+ })
1146
+ query.resolve(stream)
1147
+ }
1148
+
1149
+ /* c8 ignore next 3 */
1150
+ function CopyBothResponse() {
1151
+ stream = new Stream.Duplex({
1152
+ autoDestroy: true,
1153
+ read() { socket.resume() },
1154
+ /* c8 ignore next 11 */
1155
+ write(chunk, encoding, callback) {
1156
+ socket.write(b().d().raw(chunk).end(), callback)
1157
+ },
1158
+ destroy(error, callback) {
1159
+ callback(error)
1160
+ socket && socket.readyState === 'open' && socket.write(b().f().str(error + b.N).end())
1161
+ stream = null
1162
+ },
1163
+ final(callback) {
1164
+ socket.write(b().c().end())
1165
+ final = callback
1166
+ }
1167
+ })
1168
+ query.resolve(stream)
1169
+ }
1170
+
1171
+ function CopyData(x) {
1172
+ stream && (stream.push(x.subarray(5)) || socket.pause())
1173
+ }
1174
+
1175
+ function CopyDone() {
1176
+ stream && stream.push(null)
1177
+ stream = null
1178
+ }
1179
+
1180
+ function NoticeResponse(x) {
1181
+ onnotice
1182
+ ? onnotice(parseError(x))
1183
+ : console.log(parseError(x)) // eslint-disable-line
1184
+
1185
+ }
1186
+
1187
+ /* c8 ignore next 3 */
1188
+ function EmptyQueryResponse() {
1189
+ /* noop */
1190
+ }
1191
+
1192
+ /* c8 ignore next 3 */
1193
+ function FunctionCallResponse() {
1194
+ errored(Errors.notSupported('FunctionCallResponse'))
1195
+ }
1196
+
1197
+ /* c8 ignore next 3 */
1198
+ function NegotiateProtocolVersion() {
1199
+ errored(Errors.notSupported('NegotiateProtocolVersion'))
1200
+ }
1201
+
1202
+ /* c8 ignore next 3 */
1203
+ function UnknownMessage(x) {
1204
+ console.error('Postgres.js : Unknown Message:', x[0]) // eslint-disable-line
1205
+ }
1206
+
1207
+ /* c8 ignore next 3 */
1208
+ function UnknownAuth(x, type) {
1209
+ console.error('Postgres.js : Unknown Auth:', type) // eslint-disable-line
1210
+ }
1211
+
1212
+ /* Messages */
1213
+ function Bind(parameters, types, statement = '', portal = '') {
1214
+ let prev
1215
+ , type
1216
+
1217
+ b().B().str(portal + b.N).str(statement + b.N).i16(0).i16(parameters.length)
1218
+
1219
+ parameters.forEach((x, i) => {
1220
+ if (x === null)
1221
+ return b.i32(0xFFFFFFFF)
1222
+
1223
+ type = types[i]
1224
+ x = type in options.serializers
1225
+ ? options.serializers[type](x)
1226
+ : '' + x
1227
+
1228
+ prev = b.i
1229
+ b.inc(4).str(x).i32(b.i - prev - 4, prev)
1230
+ })
1231
+
1232
+ b.i16(0)
1233
+
1234
+ return b.end()
1235
+ }
1236
+
1237
+ function Parse(str, parameters, types, name = '') {
1238
+ b().P().str(name + b.N).str(str + b.N).i16(parameters.length)
1239
+ parameters.forEach((x, i) => b.i32(types[i] || 0))
1240
+ return b.end()
1241
+ }
1242
+
1243
+ function Describe(x, name = '') {
1244
+ return b().D().str(x).str(name + b.N).end()
1245
+ }
1246
+
1247
+ function Execute(portal = '', rows = 0) {
1248
+ return Buffer.concat([
1249
+ b().E().str(portal + b.N).i32(rows).end(),
1250
+ Flush
1251
+ ])
1252
+ }
1253
+
1254
+ function Close(portal = '') {
1255
+ return Buffer.concat([
1256
+ b().C().str('P').str(portal + b.N).end(),
1257
+ b().S().end()
1258
+ ])
1259
+ }
1260
+
1261
+ function StartupMessage() {
1262
+ return b().inc(4).i16(3).z(2).str(
1263
+ Object.entries(Object.assign({
1264
+ user,
1265
+ database,
1266
+ client_encoding: 'UTF8'
1267
+ },
1268
+ options.connection
1269
+ )).filter(([, v]) => v).map(([k, v]) => k + b.N + v).join(b.N)
1270
+ ).z(2).end(0)
1271
+ }
1272
+
1273
+ }
1274
+
1275
+ function parseError(x) {
1276
+ const error = {}
1277
+ let start = 5
1278
+ for (let i = 5; i < x.length - 1; i++) {
1279
+ if (x[i] === 0) {
1280
+ error[errorFields[x[start]]] = x.toString('utf8', start + 1, i)
1281
+ start = i + 1
1282
+ }
1283
+ }
1284
+ return error
1285
+ }
1286
+
1287
+ function md5(x) {
1288
+ return crypto.createHash('md5').update(x).digest('hex')
1289
+ }
1290
+
1291
+ function hmac(key, x) {
1292
+ return crypto.createHmac('sha256', key).update(x).digest()
1293
+ }
1294
+
1295
+ function sha256(x) {
1296
+ return crypto.createHash('sha256').update(x).digest()
1297
+ }
1298
+
1299
+ function xor(a, b) {
1300
+ const length = Math.max(a.length, b.length)
1301
+ const buffer = Buffer.allocUnsafe(length)
1302
+ for (let i = 0; i < length; i++)
1303
+ buffer[i] = a[i] ^ b[i]
1304
+ return buffer
1305
+ }
1306
+
1307
+ function timer(fn, seconds) {
1308
+ seconds = typeof seconds === 'function' ? seconds() : seconds
1309
+ if (!seconds)
1310
+ return { cancel: noop, start: noop }
1311
+
1312
+ let timer
1313
+ return {
1314
+ cancel() {
1315
+ timer && (clearTimeout(timer), timer = null)
1316
+ },
1317
+ start(...args) {
1318
+ timer && clearTimeout(timer)
1319
+ timer = setTimeout(() => done(args), seconds * 1000)
1320
+ }
1321
+ }
1322
+
1323
+ function done(args) {
1324
+ timer = null
1325
+ fn.apply(null, args)
1326
+ }
1327
+ }