neopg 0.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.
@@ -0,0 +1,1042 @@
1
+ const net = require('net')
2
+ const tls = require('tls')
3
+ const crypto = require('crypto')
4
+ const Stream = require('stream')
5
+ const { performance } = require('perf_hooks')
6
+
7
+ const { stringify, handleValue, arrayParser, arraySerializer } = require('./types.js')
8
+ const { Errors } = require('./errors.js')
9
+ const Result = require('./result.js')
10
+ const Queue = require('./queue.js')
11
+ const { Query, CLOSE } = require('./query.js')
12
+ const b = require('./bytes.js')
13
+
14
+ module.exports = Connection
15
+
16
+ let uid = 1
17
+
18
+ const Sync = b().S().end()
19
+ , Flush = b().H().end()
20
+ , SSLRequest = b().i32(8).i32(80877103).end(8)
21
+ , ExecuteUnnamed = Buffer.concat([b().E().str(b.N).i32(0).end(), Sync])
22
+ , DescribeUnnamed = b().D().str('S').str(b.N).end()
23
+ , noop = () => { /* noop */ }
24
+
25
+ const retryRoutines = new Set([
26
+ 'FetchPreparedStatement',
27
+ 'RevalidateCachedQuery',
28
+ 'transformAssignedExpr'
29
+ ])
30
+
31
+ const errorFields = {
32
+ 83 : 'severity_local', // S
33
+ 86 : 'severity', // V
34
+ 67 : 'code', // C
35
+ 77 : 'message', // M
36
+ 68 : 'detail', // D
37
+ 72 : 'hint', // H
38
+ 80 : 'position', // P
39
+ 112 : 'internal_position', // p
40
+ 113 : 'internal_query', // q
41
+ 87 : 'where', // W
42
+ 115 : 'schema_name', // s
43
+ 116 : 'table_name', // t
44
+ 99 : 'column_name', // c
45
+ 100 : 'data type_name', // d
46
+ 110 : 'constraint_name', // n
47
+ 70 : 'file', // F
48
+ 76 : 'line', // L
49
+ 82 : 'routine' // R
50
+ }
51
+
52
+ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose = noop } = {}) {
53
+ const {
54
+ ssl,
55
+ max,
56
+ user,
57
+ host,
58
+ port,
59
+ database,
60
+ parsers,
61
+ transform,
62
+ onnotice,
63
+ onnotify,
64
+ onparameter,
65
+ max_pipeline,
66
+ keep_alive,
67
+ backoff,
68
+ target_session_attrs
69
+ } = options
70
+
71
+ const sent = Queue()
72
+ , id = uid++
73
+ , backend = { pid: null, secret: null }
74
+ , idleTimer = timer(end, options.idle_timeout)
75
+ , lifeTimer = timer(end, options.max_lifetime)
76
+ , connectTimer = timer(connectTimedOut, options.connect_timeout)
77
+
78
+ let socket = null
79
+ , cancelMessage
80
+ , result = new Result()
81
+ , incoming = Buffer.alloc(0)
82
+ , needsTypes = options.fetch_types
83
+ , backendParameters = {}
84
+ , statements = {}
85
+ , statementId = Math.random().toString(36).slice(2)
86
+ , statementCount = 1
87
+ , closedDate = 0
88
+ , remaining = 0
89
+ , hostIndex = 0
90
+ , retries = 0
91
+ , length = 0
92
+ , delay = 0
93
+ , rows = 0
94
+ , serverSignature = null
95
+ , nextWriteTimer = null
96
+ , terminated = false
97
+ , incomings = null
98
+ , results = null
99
+ , initial = null
100
+ , ending = null
101
+ , stream = null
102
+ , chunk = null
103
+ , ended = null
104
+ , nonce = null
105
+ , query = null
106
+ , final = null
107
+
108
+ const connection = {
109
+ queue: queues.closed,
110
+ idleTimer,
111
+ connect(query) {
112
+ initial = query
113
+ reconnect()
114
+ },
115
+ terminate,
116
+ execute,
117
+ cancel,
118
+ end,
119
+ count: 0,
120
+ id
121
+ }
122
+
123
+ queues.closed && queues.closed.push(connection)
124
+
125
+ return connection
126
+
127
+ async function createSocket() {
128
+ let x
129
+ try {
130
+ x = options.socket
131
+ ? (await Promise.resolve(options.socket(options)))
132
+ : new net.Socket()
133
+ } catch (e) {
134
+ error(e)
135
+ return
136
+ }
137
+ x.on('error', error)
138
+ x.on('close', closed)
139
+ x.on('drain', drain)
140
+ return x
141
+ }
142
+
143
+ async function cancel({ pid, secret }, resolve, reject) {
144
+ try {
145
+ cancelMessage = b().i32(16).i32(80877102).i32(pid).i32(secret).end(16)
146
+ await connect()
147
+ socket.once('error', reject)
148
+ socket.once('close', resolve)
149
+ } catch (error) {
150
+ reject(error)
151
+ }
152
+ }
153
+
154
+ function execute(q) {
155
+ if (terminated)
156
+ return queryError(q, Errors.connection('CONNECTION_DESTROYED', options))
157
+
158
+ if (q.cancelled)
159
+ return
160
+
161
+ try {
162
+ q.state = backend
163
+ query
164
+ ? sent.push(q)
165
+ : (query = q, query.active = true)
166
+
167
+ build(q)
168
+ return write(toBuffer(q))
169
+ && !q.describeFirst
170
+ && !q.cursorFn
171
+ && sent.length < max_pipeline
172
+ && (!q.options.onexecute || q.options.onexecute(connection))
173
+ } catch (error) {
174
+ sent.length === 0 && write(Sync)
175
+ errored(error)
176
+ return true
177
+ }
178
+ }
179
+
180
+ function toBuffer(q) {
181
+ if (q.parameters.length >= 65534)
182
+ throw Errors.generic('MAX_PARAMETERS_EXCEEDED', 'Max number of parameters (65534) exceeded')
183
+
184
+ return q.options.simple
185
+ ? b().Q().str(q.statement.string + b.N).end()
186
+ : q.describeFirst
187
+ ? Buffer.concat([describe(q), Flush])
188
+ : q.prepare
189
+ ? q.prepared
190
+ ? prepared(q)
191
+ : Buffer.concat([describe(q), prepared(q)])
192
+ : unnamed(q)
193
+ }
194
+
195
+ function describe(q) {
196
+ return Buffer.concat([
197
+ Parse(q.statement.string, q.parameters, q.statement.types, q.statement.name),
198
+ Describe('S', q.statement.name)
199
+ ])
200
+ }
201
+
202
+ function prepared(q) {
203
+ return Buffer.concat([
204
+ Bind(q.parameters, q.statement.types, q.statement.name, q.cursorName),
205
+ q.cursorFn
206
+ ? Execute('', q.cursorRows)
207
+ : ExecuteUnnamed
208
+ ])
209
+ }
210
+
211
+ function unnamed(q) {
212
+ return Buffer.concat([
213
+ Parse(q.statement.string, q.parameters, q.statement.types),
214
+ DescribeUnnamed,
215
+ prepared(q)
216
+ ])
217
+ }
218
+
219
+ function build(q) {
220
+ const parameters = []
221
+ , types = []
222
+
223
+ const string = stringify(q, q.strings[0], q.args[0], parameters, types, options)
224
+
225
+ !q.tagged && q.args.forEach(x => handleValue(x, parameters, types, options))
226
+
227
+ q.prepare = options.prepare && ('prepare' in q.options ? q.options.prepare : true)
228
+ q.string = string
229
+ q.signature = q.prepare && types + string
230
+ q.onlyDescribe && (delete statements[q.signature])
231
+ q.parameters = q.parameters || parameters
232
+ q.prepared = q.prepare && q.signature in statements
233
+ q.describeFirst = q.onlyDescribe || (parameters.length && !q.prepared)
234
+ q.statement = q.prepared
235
+ ? statements[q.signature]
236
+ : { string, types, name: q.prepare ? statementId + statementCount++ : '' }
237
+
238
+ typeof options.debug === 'function' && options.debug(id, string, parameters, types)
239
+ }
240
+
241
+ function write(x, fn) {
242
+ chunk = chunk ? Buffer.concat([chunk, x]) : Buffer.from(x)
243
+ if (fn || chunk.length >= 1024)
244
+ return nextWrite(fn)
245
+ nextWriteTimer === null && (nextWriteTimer = setImmediate(nextWrite))
246
+ return true
247
+ }
248
+
249
+ function nextWrite(fn) {
250
+ const x = socket.write(chunk, fn)
251
+ nextWriteTimer !== null && clearImmediate(nextWriteTimer)
252
+ chunk = nextWriteTimer = null
253
+ return x
254
+ }
255
+
256
+ function connectTimedOut() {
257
+ errored(Errors.connection('CONNECT_TIMEOUT', options, socket))
258
+ socket.destroy()
259
+ }
260
+
261
+ async function secure() {
262
+ write(SSLRequest)
263
+ const canSSL = await new Promise(r => socket.once('data', x => r(x[0] === 83))) // S
264
+
265
+ if (!canSSL && ssl === 'prefer')
266
+ return connected()
267
+
268
+ socket.removeAllListeners()
269
+ socket = tls.connect({
270
+ socket,
271
+ servername: net.isIP(socket.host) ? undefined : socket.host,
272
+ ...(ssl === 'require' || ssl === 'allow' || ssl === 'prefer'
273
+ ? { rejectUnauthorized: false }
274
+ : ssl === 'verify-full'
275
+ ? {}
276
+ : typeof ssl === 'object'
277
+ ? ssl
278
+ : {}
279
+ )
280
+ })
281
+ socket.on('secureConnect', connected)
282
+ socket.on('error', error)
283
+ socket.on('close', closed)
284
+ socket.on('drain', drain)
285
+ }
286
+
287
+ /* c8 ignore next 3 */
288
+ function drain() {
289
+ !query && onopen(connection)
290
+ }
291
+
292
+ function data(x) {
293
+ if (incomings) {
294
+ incomings.push(x)
295
+ remaining -= x.length
296
+ if (remaining > 0)
297
+ return
298
+ }
299
+
300
+ incoming = incomings
301
+ ? Buffer.concat(incomings, length - remaining)
302
+ : incoming.length === 0
303
+ ? x
304
+ : Buffer.concat([incoming, x], incoming.length + x.length)
305
+
306
+ while (incoming.length > 4) {
307
+ length = incoming.readUInt32BE(1)
308
+ if (length >= incoming.length) {
309
+ remaining = length - incoming.length
310
+ incomings = [incoming]
311
+ break
312
+ }
313
+
314
+ try {
315
+ handle(incoming.subarray(0, length + 1))
316
+ } catch (e) {
317
+ query && (query.cursorFn || query.describeFirst) && write(Sync)
318
+ errored(e)
319
+ }
320
+ incoming = incoming.subarray(length + 1)
321
+ remaining = 0
322
+ incomings = null
323
+ }
324
+ }
325
+
326
+ async function connect() {
327
+ terminated = false
328
+ backendParameters = {}
329
+ socket || (socket = await createSocket())
330
+
331
+ if (!socket)
332
+ return
333
+
334
+ connectTimer.start()
335
+
336
+ if (options.socket)
337
+ return ssl ? secure() : connected()
338
+
339
+ socket.on('connect', ssl ? secure : connected)
340
+
341
+ if (options.path)
342
+ return socket.connect(options.path)
343
+
344
+ socket.ssl = ssl
345
+ socket.connect(port[hostIndex], host[hostIndex])
346
+ socket.host = host[hostIndex]
347
+ socket.port = port[hostIndex]
348
+
349
+ hostIndex = (hostIndex + 1) % port.length
350
+ }
351
+
352
+ function reconnect() {
353
+ setTimeout(connect, closedDate ? closedDate + delay - performance.now() : 0)
354
+ }
355
+
356
+ function connected() {
357
+ try {
358
+ statements = {}
359
+ needsTypes = options.fetch_types
360
+ statementId = Math.random().toString(36).slice(2)
361
+ statementCount = 1
362
+ lifeTimer.start()
363
+ socket.on('data', data)
364
+ keep_alive && socket.setKeepAlive && socket.setKeepAlive(true, 1000 * keep_alive)
365
+ const s = StartupMessage()
366
+ write(s)
367
+ } catch (err) {
368
+ error(err)
369
+ }
370
+ }
371
+
372
+ function error(err) {
373
+ if (connection.queue === queues.connecting && options.host[retries + 1])
374
+ return
375
+
376
+ errored(err)
377
+ while (sent.length)
378
+ queryError(sent.shift(), err)
379
+ }
380
+
381
+ function errored(err) {
382
+ stream && (stream.destroy(err), stream = null)
383
+ query && queryError(query, err)
384
+ initial && (queryError(initial, err), initial = null)
385
+ }
386
+
387
+ function queryError(query, err) {
388
+ if (query.reserve)
389
+ return query.reject(err)
390
+
391
+ if (!err || typeof err !== 'object')
392
+ err = new Error(err)
393
+
394
+ 'query' in err || 'parameters' in err || Object.defineProperties(err, {
395
+ stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug },
396
+ query: { value: query.string, enumerable: options.debug },
397
+ parameters: { value: query.parameters, enumerable: options.debug },
398
+ args: { value: query.args, enumerable: options.debug },
399
+ types: { value: query.statement && query.statement.types, enumerable: options.debug }
400
+ })
401
+ query.reject(err)
402
+ }
403
+
404
+ function end() {
405
+ return ending || (
406
+ !connection.reserved && onend(connection),
407
+ !connection.reserved && !initial && !query && sent.length === 0
408
+ ? (terminate(), new Promise(r => socket && socket.readyState !== 'closed' ? socket.once('close', r) : r()))
409
+ : ending = new Promise(r => ended = r)
410
+ )
411
+ }
412
+
413
+ function terminate() {
414
+ terminated = true
415
+ if (stream || query || initial || sent.length)
416
+ error(Errors.connection('CONNECTION_DESTROYED', options))
417
+
418
+ clearImmediate(nextWriteTimer)
419
+ if (socket) {
420
+ socket.removeListener('data', data)
421
+ socket.removeListener('connect', connected)
422
+ socket.readyState === 'open' && socket.end(b().X().end())
423
+ }
424
+ ended && (ended(), ending = ended = null)
425
+ }
426
+
427
+ async function closed(hadError) {
428
+ incoming = Buffer.alloc(0)
429
+ remaining = 0
430
+ incomings = null
431
+ clearImmediate(nextWriteTimer)
432
+ socket.removeListener('data', data)
433
+ socket.removeListener('connect', connected)
434
+ idleTimer.cancel()
435
+ lifeTimer.cancel()
436
+ connectTimer.cancel()
437
+
438
+ socket.removeAllListeners()
439
+ socket = null
440
+
441
+ if (initial)
442
+ return reconnect()
443
+
444
+ !hadError && (query || sent.length) && error(Errors.connection('CONNECTION_CLOSED', options, socket))
445
+ closedDate = performance.now()
446
+ hadError && options.shared.retries++
447
+ delay = (typeof backoff === 'function' ? backoff(options.shared.retries) : backoff) * 1000
448
+ onclose(connection, Errors.connection('CONNECTION_CLOSED', options, socket))
449
+ }
450
+
451
+ /* Handlers */
452
+ function handle(xs, x = xs[0]) {
453
+ (
454
+ x === 68 ? DataRow : // D
455
+ x === 100 ? CopyData : // d
456
+ x === 65 ? NotificationResponse : // A
457
+ x === 83 ? ParameterStatus : // S
458
+ x === 90 ? ReadyForQuery : // Z
459
+ x === 67 ? CommandComplete : // C
460
+ x === 50 ? BindComplete : // 2
461
+ x === 49 ? ParseComplete : // 1
462
+ x === 116 ? ParameterDescription : // t
463
+ x === 84 ? RowDescription : // T
464
+ x === 82 ? Authentication : // R
465
+ x === 110 ? NoData : // n
466
+ x === 75 ? BackendKeyData : // K
467
+ x === 69 ? ErrorResponse : // E
468
+ x === 115 ? PortalSuspended : // s
469
+ x === 51 ? CloseComplete : // 3
470
+ x === 71 ? CopyInResponse : // G
471
+ x === 78 ? NoticeResponse : // N
472
+ x === 72 ? CopyOutResponse : // H
473
+ x === 99 ? CopyDone : // c
474
+ x === 73 ? EmptyQueryResponse : // I
475
+ x === 86 ? FunctionCallResponse : // V
476
+ x === 118 ? NegotiateProtocolVersion : // v
477
+ x === 87 ? CopyBothResponse : // W
478
+ /* c8 ignore next */
479
+ UnknownMessage
480
+ )(xs)
481
+ }
482
+
483
+ function DataRow(x) {
484
+ let index = 7
485
+ let length
486
+ let column
487
+ let value
488
+
489
+ const row = query.isRaw ? new Array(query.statement.columns.length) : {}
490
+ for (let i = 0; i < query.statement.columns.length; i++) {
491
+ column = query.statement.columns[i]
492
+ length = x.readInt32BE(index)
493
+ index += 4
494
+
495
+ value = length === -1
496
+ ? null
497
+ : query.isRaw === true
498
+ ? x.subarray(index, index += length)
499
+ : column.parser === undefined
500
+ ? x.toString('utf8', index, index += length)
501
+ : column.parser.array === true
502
+ ? column.parser(x.toString('utf8', index + 1, index += length))
503
+ : column.parser(x.toString('utf8', index, index += length))
504
+
505
+ query.isRaw
506
+ ? (row[i] = query.isRaw === true
507
+ ? value
508
+ : transform.value.from ? transform.value.from(value, column) : value)
509
+ : (row[column.name] = transform.value.from ? transform.value.from(value, column) : value)
510
+ }
511
+
512
+ query.forEachFn
513
+ ? query.forEachFn(transform.row.from ? transform.row.from(row) : row, result)
514
+ : (result[rows++] = transform.row.from ? transform.row.from(row) : row)
515
+ }
516
+
517
+ function ParameterStatus(x) {
518
+ const [k, v] = x.toString('utf8', 5, x.length - 1).split(b.N)
519
+ backendParameters[k] = v
520
+ if (options.parameters[k] !== v) {
521
+ options.parameters[k] = v
522
+ onparameter && onparameter(k, v)
523
+ }
524
+ }
525
+
526
+ function ReadyForQuery(x) {
527
+ query && query.options.simple && query.resolve(results || result)
528
+ query = results = null
529
+ result = new Result()
530
+ connectTimer.cancel()
531
+
532
+ if (initial) {
533
+ if (target_session_attrs) {
534
+ if (!backendParameters.in_hot_standby || !backendParameters.default_transaction_read_only)
535
+ return fetchState()
536
+ else if (tryNext(target_session_attrs, backendParameters))
537
+ return terminate()
538
+ }
539
+
540
+ if (needsTypes) {
541
+ initial.reserve && (initial = null)
542
+ return fetchArrayTypes()
543
+ }
544
+
545
+ initial && !initial.reserve && execute(initial)
546
+ options.shared.retries = retries = 0
547
+ initial = null
548
+ return
549
+ }
550
+
551
+ while (sent.length && (query = sent.shift()) && (query.active = true, query.cancelled))
552
+ Connection(options).cancel(query.state, query.cancelled.resolve, query.cancelled.reject)
553
+
554
+ if (query)
555
+ return // Consider opening if able and sent.length < 50
556
+
557
+ connection.reserved
558
+ ? !connection.reserved.release && x[5] === 73 // I
559
+ ? ending
560
+ ? terminate()
561
+ : (connection.reserved = null, onopen(connection))
562
+ : connection.reserved()
563
+ : ending
564
+ ? terminate()
565
+ : onopen(connection)
566
+ }
567
+
568
+ function CommandComplete(x) {
569
+ rows = 0
570
+
571
+ for (let i = x.length - 1; i > 0; i--) {
572
+ if (x[i] === 32 && x[i + 1] < 58 && result.count === null)
573
+ result.count = +x.toString('utf8', i + 1, x.length - 1)
574
+ if (x[i - 1] >= 65) {
575
+ result.command = x.toString('utf8', 5, i)
576
+ result.state = backend
577
+ break
578
+ }
579
+ }
580
+
581
+ final && (final(), final = null)
582
+
583
+ if (result.command === 'BEGIN' && max !== 1 && !connection.reserved)
584
+ return errored(Errors.generic('UNSAFE_TRANSACTION', 'Only use sql.begin, sql.reserved or max: 1'))
585
+
586
+ if (query.options.simple)
587
+ return BindComplete()
588
+
589
+ if (query.cursorFn) {
590
+ result.count && query.cursorFn(result)
591
+ write(Sync)
592
+ }
593
+
594
+ query.resolve(result)
595
+ }
596
+
597
+ function ParseComplete() {
598
+ query.parsing = false
599
+ }
600
+
601
+ function BindComplete() {
602
+ !result.statement && (result.statement = query.statement)
603
+ result.columns = query.statement.columns
604
+ }
605
+
606
+ function ParameterDescription(x) {
607
+ const length = x.readUInt16BE(5)
608
+
609
+ for (let i = 0; i < length; ++i)
610
+ !query.statement.types[i] && (query.statement.types[i] = x.readUInt32BE(7 + i * 4))
611
+
612
+ query.prepare && (statements[query.signature] = query.statement)
613
+ query.describeFirst && !query.onlyDescribe && (write(prepared(query)), query.describeFirst = false)
614
+ }
615
+
616
+ function RowDescription(x) {
617
+ if (result.command) {
618
+ results = results || [result]
619
+ results.push(result = new Result())
620
+ result.count = null
621
+ query.statement.columns = null
622
+ }
623
+
624
+ const length = x.readUInt16BE(5)
625
+ let index = 7
626
+ let start
627
+
628
+ query.statement.columns = Array(length)
629
+
630
+ for (let i = 0; i < length; ++i) {
631
+ start = index
632
+ while (x[index++] !== 0);
633
+ const table = x.readUInt32BE(index)
634
+ const number = x.readUInt16BE(index + 4)
635
+ const type = x.readUInt32BE(index + 6)
636
+ query.statement.columns[i] = {
637
+ name: transform.column.from
638
+ ? transform.column.from(x.toString('utf8', start, index - 1))
639
+ : x.toString('utf8', start, index - 1),
640
+ parser: parsers[type],
641
+ table,
642
+ number,
643
+ type
644
+ }
645
+ index += 18
646
+ }
647
+
648
+ result.statement = query.statement
649
+ if (query.onlyDescribe)
650
+ return (query.resolve(query.statement), write(Sync))
651
+ }
652
+
653
+ async function Authentication(x, type = x.readUInt32BE(5)) {
654
+ (
655
+ type === 3 ? AuthenticationCleartextPassword :
656
+ type === 5 ? AuthenticationMD5Password :
657
+ type === 10 ? SASL :
658
+ type === 11 ? SASLContinue :
659
+ type === 12 ? SASLFinal :
660
+ type !== 0 ? UnknownAuth :
661
+ noop
662
+ )(x, type)
663
+ }
664
+
665
+ /* c8 ignore next 5 */
666
+ async function AuthenticationCleartextPassword() {
667
+ const payload = await Pass()
668
+ write(
669
+ b().p().str(payload).z(1).end()
670
+ )
671
+ }
672
+
673
+ async function AuthenticationMD5Password(x) {
674
+ const payload = 'md5' + (
675
+ await md5(
676
+ Buffer.concat([
677
+ Buffer.from(await md5((await Pass()) + user)),
678
+ x.subarray(9)
679
+ ])
680
+ )
681
+ )
682
+ write(
683
+ b().p().str(payload).z(1).end()
684
+ )
685
+ }
686
+
687
+ async function SASL() {
688
+ nonce = (await crypto.randomBytes(18)).toString('base64')
689
+ b().p().str('SCRAM-SHA-256' + b.N)
690
+ const i = b.i
691
+ write(b.inc(4).str('n,,n=*,r=' + nonce).i32(b.i - i - 4, i).end())
692
+ }
693
+
694
+ async function SASLContinue(x) {
695
+ const res = x.toString('utf8', 9).split(',').reduce((acc, x) => (acc[x[0]] = x.slice(2), acc), {})
696
+
697
+ const saltedPassword = await crypto.pbkdf2Sync(
698
+ await Pass(),
699
+ Buffer.from(res.s, 'base64'),
700
+ parseInt(res.i), 32,
701
+ 'sha256'
702
+ )
703
+
704
+ const clientKey = await hmac(saltedPassword, 'Client Key')
705
+
706
+ const auth = 'n=*,r=' + nonce + ','
707
+ + 'r=' + res.r + ',s=' + res.s + ',i=' + res.i
708
+ + ',c=biws,r=' + res.r
709
+
710
+ serverSignature = (await hmac(await hmac(saltedPassword, 'Server Key'), auth)).toString('base64')
711
+
712
+ const payload = 'c=biws,r=' + res.r + ',p=' + xor(
713
+ clientKey, Buffer.from(await hmac(await sha256(clientKey), auth))
714
+ ).toString('base64')
715
+
716
+ write(
717
+ b().p().str(payload).end()
718
+ )
719
+ }
720
+
721
+ function SASLFinal(x) {
722
+ if (x.toString('utf8', 9).split(b.N, 1)[0].slice(2) === serverSignature)
723
+ return
724
+ /* c8 ignore next 5 */
725
+ errored(Errors.generic('SASL_SIGNATURE_MISMATCH', 'The server did not return the correct signature'))
726
+ socket.destroy()
727
+ }
728
+
729
+ function Pass() {
730
+ return Promise.resolve(typeof options.pass === 'function'
731
+ ? options.pass()
732
+ : options.pass
733
+ )
734
+ }
735
+
736
+ function NoData() {
737
+ result.statement = query.statement
738
+ result.statement.columns = []
739
+ if (query.onlyDescribe)
740
+ return (query.resolve(query.statement), write(Sync))
741
+ }
742
+
743
+ function BackendKeyData(x) {
744
+ backend.pid = x.readUInt32BE(5)
745
+ backend.secret = x.readUInt32BE(9)
746
+ }
747
+
748
+ async function fetchArrayTypes() {
749
+ needsTypes = false
750
+ const types = await new Query([`
751
+ select b.oid, b.typarray
752
+ from pg_catalog.pg_type a
753
+ left join pg_catalog.pg_type b on b.oid = a.typelem
754
+ where a.typcategory = 'A'
755
+ group by b.oid, b.typarray
756
+ order by b.oid
757
+ `], [], execute)
758
+ types.forEach(({ oid, typarray }) => addArrayType(oid, typarray))
759
+ }
760
+
761
+ function addArrayType(oid, typarray) {
762
+ if (!!options.parsers[typarray] && !!options.serializers[typarray]) return
763
+ const parser = options.parsers[oid]
764
+ options.shared.typeArrayMap[oid] = typarray
765
+ options.parsers[typarray] = (xs) => arrayParser(xs, parser, typarray)
766
+ options.parsers[typarray].array = true
767
+ options.serializers[typarray] = (xs) => arraySerializer(xs, options.serializers[oid], options, typarray)
768
+ }
769
+
770
+ function tryNext(x, xs) {
771
+ return (
772
+ (x === 'read-write' && xs.default_transaction_read_only === 'on') ||
773
+ (x === 'read-only' && xs.default_transaction_read_only === 'off') ||
774
+ (x === 'primary' && xs.in_hot_standby === 'on') ||
775
+ (x === 'standby' && xs.in_hot_standby === 'off') ||
776
+ (x === 'prefer-standby' && xs.in_hot_standby === 'off' && options.host[retries])
777
+ )
778
+ }
779
+
780
+ function fetchState() {
781
+ const query = new Query([`
782
+ show transaction_read_only;
783
+ select pg_catalog.pg_is_in_recovery()
784
+ `], [], execute, null, { simple: true })
785
+ query.resolve = ([[a], [b]]) => {
786
+ backendParameters.default_transaction_read_only = a.transaction_read_only
787
+ backendParameters.in_hot_standby = b.pg_is_in_recovery ? 'on' : 'off'
788
+ }
789
+ query.execute()
790
+ }
791
+
792
+ function ErrorResponse(x) {
793
+ query && (query.cursorFn || query.describeFirst) && write(Sync)
794
+ const error = Errors.postgres(parseError(x))
795
+ query && query.retried
796
+ ? errored(query.retried)
797
+ : query && query.prepared && retryRoutines.has(error.routine)
798
+ ? retry(query, error)
799
+ : errored(error)
800
+ }
801
+
802
+ function retry(q, error) {
803
+ delete statements[q.signature]
804
+ q.retried = error
805
+ execute(q)
806
+ }
807
+
808
+ function NotificationResponse(x) {
809
+ if (!onnotify)
810
+ return
811
+
812
+ let index = 9
813
+ while (x[index++] !== 0);
814
+ onnotify(
815
+ x.toString('utf8', 9, index - 1),
816
+ x.toString('utf8', index, x.length - 1)
817
+ )
818
+ }
819
+
820
+ async function PortalSuspended() {
821
+ try {
822
+ const x = await Promise.resolve(query.cursorFn(result))
823
+ rows = 0
824
+ x === CLOSE
825
+ ? write(Close(query.portal))
826
+ : (result = new Result(), write(Execute('', query.cursorRows)))
827
+ } catch (err) {
828
+ write(Sync)
829
+ query.reject(err)
830
+ }
831
+ }
832
+
833
+ function CloseComplete() {
834
+ result.count && query.cursorFn(result)
835
+ query.resolve(result)
836
+ }
837
+
838
+ function CopyInResponse() {
839
+ stream = new Stream.Writable({
840
+ autoDestroy: true,
841
+ write(chunk, encoding, callback) {
842
+ socket.write(b().d().raw(chunk).end(), callback)
843
+ },
844
+ destroy(error, callback) {
845
+ callback(error)
846
+ socket.write(b().f().str(error + b.N).end())
847
+ stream = null
848
+ },
849
+ final(callback) {
850
+ socket.write(b().c().end())
851
+ final = callback
852
+ }
853
+ })
854
+ query.resolve(stream)
855
+ }
856
+
857
+ function CopyOutResponse() {
858
+ stream = new Stream.Readable({
859
+ read() { socket.resume() }
860
+ })
861
+ query.resolve(stream)
862
+ }
863
+
864
+ /* c8 ignore next 3 */
865
+ function CopyBothResponse() {
866
+ stream = new Stream.Duplex({
867
+ autoDestroy: true,
868
+ read() { socket.resume() },
869
+ /* c8 ignore next 11 */
870
+ write(chunk, encoding, callback) {
871
+ socket.write(b().d().raw(chunk).end(), callback)
872
+ },
873
+ destroy(error, callback) {
874
+ callback(error)
875
+ socket.write(b().f().str(error + b.N).end())
876
+ stream = null
877
+ },
878
+ final(callback) {
879
+ socket.write(b().c().end())
880
+ final = callback
881
+ }
882
+ })
883
+ query.resolve(stream)
884
+ }
885
+
886
+ function CopyData(x) {
887
+ stream && (stream.push(x.subarray(5)) || socket.pause())
888
+ }
889
+
890
+ function CopyDone() {
891
+ stream && stream.push(null)
892
+ stream = null
893
+ }
894
+
895
+ function NoticeResponse(x) {
896
+ onnotice
897
+ ? onnotice(parseError(x))
898
+ : console.log(parseError(x)) // eslint-disable-line
899
+
900
+ }
901
+
902
+ /* c8 ignore next 3 */
903
+ function EmptyQueryResponse() {
904
+ /* noop */
905
+ }
906
+
907
+ /* c8 ignore next 3 */
908
+ function FunctionCallResponse() {
909
+ errored(Errors.notSupported('FunctionCallResponse'))
910
+ }
911
+
912
+ /* c8 ignore next 3 */
913
+ function NegotiateProtocolVersion() {
914
+ errored(Errors.notSupported('NegotiateProtocolVersion'))
915
+ }
916
+
917
+ /* c8 ignore next 3 */
918
+ function UnknownMessage(x) {
919
+ console.error('Postgres.js : Unknown Message:', x[0]) // eslint-disable-line
920
+ }
921
+
922
+ /* c8 ignore next 3 */
923
+ function UnknownAuth(x, type) {
924
+ console.error('Postgres.js : Unknown Auth:', type) // eslint-disable-line
925
+ }
926
+
927
+ /* Messages */
928
+ function Bind(parameters, types, statement = '', portal = '') {
929
+ let prev
930
+ , type
931
+
932
+ b().B().str(portal + b.N).str(statement + b.N).i16(0).i16(parameters.length)
933
+
934
+ parameters.forEach((x, i) => {
935
+ if (x === null)
936
+ return b.i32(0xFFFFFFFF)
937
+
938
+ type = types[i]
939
+ parameters[i] = x = type in options.serializers
940
+ ? options.serializers[type](x)
941
+ : '' + x
942
+
943
+ prev = b.i
944
+ b.inc(4).str(x).i32(b.i - prev - 4, prev)
945
+ })
946
+
947
+ b.i16(0)
948
+
949
+ return b.end()
950
+ }
951
+
952
+ function Parse(str, parameters, types, name = '') {
953
+ b().P().str(name + b.N).str(str + b.N).i16(parameters.length)
954
+ parameters.forEach((x, i) => b.i32(types[i] || 0))
955
+ return b.end()
956
+ }
957
+
958
+ function Describe(x, name = '') {
959
+ return b().D().str(x).str(name + b.N).end()
960
+ }
961
+
962
+ function Execute(portal = '', rows = 0) {
963
+ return Buffer.concat([
964
+ b().E().str(portal + b.N).i32(rows).end(),
965
+ Flush
966
+ ])
967
+ }
968
+
969
+ function Close(portal = '') {
970
+ return Buffer.concat([
971
+ b().C().str('P').str(portal + b.N).end(),
972
+ b().S().end()
973
+ ])
974
+ }
975
+
976
+ function StartupMessage() {
977
+ return cancelMessage || b().inc(4).i16(3).z(2).str(
978
+ Object.entries(Object.assign({
979
+ user,
980
+ database,
981
+ client_encoding: 'UTF8'
982
+ },
983
+ options.connection
984
+ )).filter(([, v]) => v).map(([k, v]) => k + b.N + v).join(b.N)
985
+ ).z(2).end(0)
986
+ }
987
+
988
+ }
989
+
990
+ function parseError(x) {
991
+ const error = {}
992
+ let start = 5
993
+ for (let i = 5; i < x.length - 1; i++) {
994
+ if (x[i] === 0) {
995
+ error[errorFields[x[start]]] = x.toString('utf8', start + 1, i)
996
+ start = i + 1
997
+ }
998
+ }
999
+ return error
1000
+ }
1001
+
1002
+ function md5(x) {
1003
+ return crypto.createHash('md5').update(x).digest('hex')
1004
+ }
1005
+
1006
+ function hmac(key, x) {
1007
+ return crypto.createHmac('sha256', key).update(x).digest()
1008
+ }
1009
+
1010
+ function sha256(x) {
1011
+ return crypto.createHash('sha256').update(x).digest()
1012
+ }
1013
+
1014
+ function xor(a, b) {
1015
+ const length = Math.max(a.length, b.length)
1016
+ const buffer = Buffer.allocUnsafe(length)
1017
+ for (let i = 0; i < length; i++)
1018
+ buffer[i] = a[i] ^ b[i]
1019
+ return buffer
1020
+ }
1021
+
1022
+ function timer(fn, seconds) {
1023
+ seconds = typeof seconds === 'function' ? seconds() : seconds
1024
+ if (!seconds)
1025
+ return { cancel: noop, start: noop }
1026
+
1027
+ let timer
1028
+ return {
1029
+ cancel() {
1030
+ timer && (clearTimeout(timer), timer = null)
1031
+ },
1032
+ start() {
1033
+ timer && clearTimeout(timer)
1034
+ timer = setTimeout(done, seconds * 1000, arguments)
1035
+ }
1036
+ }
1037
+
1038
+ function done(args) {
1039
+ fn.apply(null, args)
1040
+ timer = null
1041
+ }
1042
+ }