@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.
- package/README.md +1419 -2
- package/UNLICENSE +24 -0
- package/cf/polyfills.js +251 -0
- package/cf/src/bytes.js +79 -0
- package/cf/src/connection.js +1327 -0
- package/cf/src/errors.js +53 -0
- package/cf/src/index.js +648 -0
- package/cf/src/large.js +70 -0
- package/cf/src/query.js +173 -0
- package/cf/src/queue.js +31 -0
- package/cf/src/result.js +16 -0
- package/cf/src/subscribe.js +278 -0
- package/cf/src/types.js +391 -0
- package/cjs/package.json +1 -0
- package/cjs/src/bytes.js +78 -0
- package/cjs/src/connection.js +1325 -0
- package/cjs/src/errors.js +53 -0
- package/cjs/src/index.js +647 -0
- package/cjs/src/large.js +70 -0
- package/cjs/src/query.js +173 -0
- package/cjs/src/queue.js +31 -0
- package/cjs/src/result.js +16 -0
- package/cjs/src/subscribe.js +277 -0
- package/cjs/src/types.js +390 -0
- package/package.json +64 -4
- package/src/bytes.js +78 -0
- package/src/connection.js +1325 -0
- package/src/errors.js +53 -0
- package/src/index.js +647 -0
- package/src/large.js +70 -0
- package/src/query.js +173 -0
- package/src/queue.js +31 -0
- package/src/result.js +16 -0
- package/src/subscribe.js +277 -0
- package/src/types.js +390 -0
- package/types/index.d.ts +735 -0
- package/types/package.json +5 -0
- package/types/tsconfig.json +14 -0
|
@@ -0,0 +1,1325 @@
|
|
|
1
|
+
import net from 'net'
|
|
2
|
+
import tls from 'tls'
|
|
3
|
+
import crypto from 'crypto'
|
|
4
|
+
import Stream from 'stream'
|
|
5
|
+
import { performance } from 'perf_hooks'
|
|
6
|
+
|
|
7
|
+
import { stringify, handleValue, addArrayType } from './types.js'
|
|
8
|
+
import { Errors } from './errors.js'
|
|
9
|
+
import Result from './result.js'
|
|
10
|
+
import Queue from './queue.js'
|
|
11
|
+
import { Query, CLOSE } from './query.js'
|
|
12
|
+
import b from './bytes.js'
|
|
13
|
+
|
|
14
|
+
export default 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 Phase = { Closed: 0, Backoff: 1, Opening: 2, Negotiating: 3, Authenticating: 4, Initializing: 5, Ready: 6, Draining: 7, Closing: 8 }
|
|
26
|
+
|
|
27
|
+
const retryRoutines = new Set([
|
|
28
|
+
'FetchPreparedStatement',
|
|
29
|
+
'RevalidateCachedQuery',
|
|
30
|
+
'transformAssignedExpr'
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
const errorFields = {
|
|
34
|
+
83 : 'severity_local', // S
|
|
35
|
+
86 : 'severity', // V
|
|
36
|
+
67 : 'code', // C
|
|
37
|
+
77 : 'message', // M
|
|
38
|
+
68 : 'detail', // D
|
|
39
|
+
72 : 'hint', // H
|
|
40
|
+
80 : 'position', // P
|
|
41
|
+
112 : 'internal_position', // p
|
|
42
|
+
113 : 'internal_query', // q
|
|
43
|
+
87 : 'where', // W
|
|
44
|
+
115 : 'schema_name', // s
|
|
45
|
+
116 : 'table_name', // t
|
|
46
|
+
99 : 'column_name', // c
|
|
47
|
+
100 : 'data type_name', // d
|
|
48
|
+
110 : 'constraint_name', // n
|
|
49
|
+
70 : 'file', // F
|
|
50
|
+
76 : 'line', // L
|
|
51
|
+
82 : 'routine' // R
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose = noop, ondrain = noop } = {}) {
|
|
55
|
+
const {
|
|
56
|
+
sslnegotiation,
|
|
57
|
+
ssl,
|
|
58
|
+
max,
|
|
59
|
+
user,
|
|
60
|
+
host,
|
|
61
|
+
port,
|
|
62
|
+
database,
|
|
63
|
+
parsers,
|
|
64
|
+
transform,
|
|
65
|
+
onnotice,
|
|
66
|
+
onnotify,
|
|
67
|
+
onparameter,
|
|
68
|
+
max_pipeline,
|
|
69
|
+
keep_alive,
|
|
70
|
+
backoff,
|
|
71
|
+
target_session_attrs
|
|
72
|
+
} = options
|
|
73
|
+
|
|
74
|
+
const sent = Queue()
|
|
75
|
+
, id = uid++
|
|
76
|
+
, backend = { pid: null, secret: null }
|
|
77
|
+
, idleTimer = timer(end, options.idle_timeout)
|
|
78
|
+
, lifeTimer = timer(end, options.max_lifetime)
|
|
79
|
+
|
|
80
|
+
let socket = null
|
|
81
|
+
, phase = Phase.Closed
|
|
82
|
+
, generation = 0
|
|
83
|
+
, acquisition = null
|
|
84
|
+
, endRequested = false
|
|
85
|
+
, endWaiters = []
|
|
86
|
+
, inheritedBackoff = null
|
|
87
|
+
, backoffTimer = null
|
|
88
|
+
, connectTimer = null
|
|
89
|
+
, errorResponse = null
|
|
90
|
+
, result = new Result()
|
|
91
|
+
, incoming = Buffer.alloc(0)
|
|
92
|
+
, needsTypes = options.fetch_types
|
|
93
|
+
, backendParameters = {}
|
|
94
|
+
, statements = {}
|
|
95
|
+
, statementId = Math.random().toString(36).slice(2)
|
|
96
|
+
, statementCount = 1
|
|
97
|
+
, remaining = 0
|
|
98
|
+
, hostIndex = 0
|
|
99
|
+
, length = 0
|
|
100
|
+
, rows = 0
|
|
101
|
+
, serverSignature = null
|
|
102
|
+
, nextWriteTimer = null
|
|
103
|
+
, incomings = null
|
|
104
|
+
, results = null
|
|
105
|
+
, stream = null
|
|
106
|
+
, chunk = null
|
|
107
|
+
, nonce = null
|
|
108
|
+
, query = null
|
|
109
|
+
, final = null
|
|
110
|
+
|
|
111
|
+
const connection = {
|
|
112
|
+
queue: queues.closed,
|
|
113
|
+
idleTimer,
|
|
114
|
+
connect: acquire,
|
|
115
|
+
terminate,
|
|
116
|
+
execute,
|
|
117
|
+
cancel,
|
|
118
|
+
release,
|
|
119
|
+
end,
|
|
120
|
+
count: 0,
|
|
121
|
+
id
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
queues.closed && queues.closed.push(connection)
|
|
125
|
+
|
|
126
|
+
return connection
|
|
127
|
+
|
|
128
|
+
function transition(next) {
|
|
129
|
+
generation++
|
|
130
|
+
phase = next
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function starting() {
|
|
134
|
+
return phase >= Phase.Opening && phase <= Phase.Initializing
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function held() {
|
|
138
|
+
return connection.reserved || connection.queue === queues.reserved
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function drained() {
|
|
142
|
+
ondrain(connection) || closing(true)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function idle() {
|
|
146
|
+
return !query && sent.length === 0
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function backoffMs() {
|
|
150
|
+
return (typeof backoff === 'function' ? backoff(options.shared.retries) : backoff) * 1000
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function acquire(owner) {
|
|
154
|
+
if (phase !== Phase.Closed)
|
|
155
|
+
return queryError(owner, Errors.connection('CONNECTION_CLOSED', options))
|
|
156
|
+
|
|
157
|
+
acquisition = { owner, attempts: 0, hostsTried: 0, lastError: null, deadline: null, expired: false }
|
|
158
|
+
const wait = inheritedBackoff ? inheritedBackoff.at + inheritedBackoff.delay - performance.now() : 0
|
|
159
|
+
inheritedBackoff = null
|
|
160
|
+
wait > 0 ? enterBackoff(wait) : enterOpening()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function enterBackoff(ms) {
|
|
164
|
+
transition(Phase.Backoff)
|
|
165
|
+
const a = acquisition
|
|
166
|
+
const attempt = generation
|
|
167
|
+
const left = a.deadline === null ? Infinity : Math.max(0, a.deadline - performance.now())
|
|
168
|
+
a.expired = left <= ms
|
|
169
|
+
backoffTimer = setTimeout(() => {
|
|
170
|
+
backoffTimer = null
|
|
171
|
+
attempt === generation && (a.expired
|
|
172
|
+
? enterClosed(a.lastError || Errors.connection('CONNECT_TIMEOUT', options))
|
|
173
|
+
: enterOpening())
|
|
174
|
+
}, Math.min(ms, left))
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function enterOpening() {
|
|
178
|
+
transition(Phase.Opening)
|
|
179
|
+
const a = acquisition
|
|
180
|
+
const attempt = generation
|
|
181
|
+
const ms = options.connect_timeout * 1000
|
|
182
|
+
if (ms) {
|
|
183
|
+
const now = performance.now()
|
|
184
|
+
a.deadline === null && (a.deadline = now + ms * host.length)
|
|
185
|
+
const left = a.deadline - now
|
|
186
|
+
connectTimer = setTimeout(() => connectTimedOut(left <= ms), Math.min(ms, left))
|
|
187
|
+
}
|
|
188
|
+
a.hostsTried++
|
|
189
|
+
backendParameters = {}
|
|
190
|
+
Promise.resolve()
|
|
191
|
+
.then(() => options.socket ? options.socket(options) : new net.Socket())
|
|
192
|
+
.then(created => attempt === generation ? attach(created) : dispose(created))
|
|
193
|
+
.catch(err => attempt === generation && fail(err))
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function dispose(created) {
|
|
197
|
+
created.on('error', noop)
|
|
198
|
+
created.destroy()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function attach(created) {
|
|
202
|
+
socket = created
|
|
203
|
+
created.on('error', error)
|
|
204
|
+
created.on('close', closed)
|
|
205
|
+
created.on('drain', drain)
|
|
206
|
+
|
|
207
|
+
if (options.socket)
|
|
208
|
+
return ssl ? negotiate() : authenticate()
|
|
209
|
+
|
|
210
|
+
created.on('connect', ssl ? negotiate : authenticate)
|
|
211
|
+
|
|
212
|
+
if (options.path)
|
|
213
|
+
return created.connect(options.path)
|
|
214
|
+
|
|
215
|
+
created.ssl = ssl
|
|
216
|
+
created.connect(port[hostIndex], host[hostIndex])
|
|
217
|
+
created.host = host[hostIndex]
|
|
218
|
+
created.port = port[hostIndex]
|
|
219
|
+
|
|
220
|
+
hostIndex = (hostIndex + 1) % port.length
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function negotiate() {
|
|
224
|
+
transition(Phase.Negotiating)
|
|
225
|
+
if (sslnegotiation === 'direct')
|
|
226
|
+
return upgrade()
|
|
227
|
+
|
|
228
|
+
const attempt = generation
|
|
229
|
+
socket.once('data', x => attempt === generation && (
|
|
230
|
+
x[0] === 83
|
|
231
|
+
? upgrade()
|
|
232
|
+
: ssl === 'prefer'
|
|
233
|
+
? authenticate()
|
|
234
|
+
: fail(Errors.generic('SSL_NOT_SUPPORTED', 'The server does not support SSL connections'))
|
|
235
|
+
))
|
|
236
|
+
write(SSLRequest)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function upgrade() {
|
|
240
|
+
try {
|
|
241
|
+
const raw = socket
|
|
242
|
+
const config = tlsConfig(raw)
|
|
243
|
+
raw.removeAllListeners()
|
|
244
|
+
socket = tls.connect(config)
|
|
245
|
+
socket.on('secureConnect', authenticate)
|
|
246
|
+
socket.on('error', error)
|
|
247
|
+
socket.on('close', closed)
|
|
248
|
+
socket.on('drain', drain)
|
|
249
|
+
} catch (err) {
|
|
250
|
+
fail(err)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function tlsConfig(raw) {
|
|
255
|
+
const config = {
|
|
256
|
+
socket: raw,
|
|
257
|
+
servername: net.isIP(raw.host) ? undefined : raw.host
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (sslnegotiation === 'direct')
|
|
261
|
+
config.ALPNProtocols = ['postgresql']
|
|
262
|
+
|
|
263
|
+
if (ssl === 'require' || ssl === 'allow' || ssl === 'prefer')
|
|
264
|
+
config.rejectUnauthorized = false
|
|
265
|
+
else if (typeof ssl === 'object')
|
|
266
|
+
Object.assign(config, ssl)
|
|
267
|
+
|
|
268
|
+
return config
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function authenticate() {
|
|
272
|
+
transition(Phase.Authenticating)
|
|
273
|
+
try {
|
|
274
|
+
statements = {}
|
|
275
|
+
needsTypes = options.fetch_types
|
|
276
|
+
statementId = Math.random().toString(36).slice(2)
|
|
277
|
+
statementCount = 1
|
|
278
|
+
lifeTimer.start()
|
|
279
|
+
socket.on('data', data)
|
|
280
|
+
keep_alive && socket.setKeepAlive && socket.setKeepAlive(true, 1000 * keep_alive)
|
|
281
|
+
write(StartupMessage())
|
|
282
|
+
} catch (err) {
|
|
283
|
+
fail(err)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function connectTimedOut(expiring) {
|
|
288
|
+
connectTimer = null
|
|
289
|
+
if (!starting())
|
|
290
|
+
return
|
|
291
|
+
expiring && (acquisition.expired = true)
|
|
292
|
+
fail(Errors.connection('CONNECT_TIMEOUT', options, socket), true)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function fail(err, persist = host.length > 1) {
|
|
296
|
+
const a = acquisition
|
|
297
|
+
a.attempts++
|
|
298
|
+
a.lastError = err && err.code === 'CONNECT_TIMEOUT' && a.lastError ? a.lastError : err
|
|
299
|
+
clearTimeout(connectTimer)
|
|
300
|
+
connectTimer = null
|
|
301
|
+
detach()
|
|
302
|
+
resetSocketState(err)
|
|
303
|
+
|
|
304
|
+
if (endRequested)
|
|
305
|
+
return enterClosed(err)
|
|
306
|
+
|
|
307
|
+
if (a.expired || (a.deadline !== null && performance.now() >= a.deadline))
|
|
308
|
+
return enterClosed(a.lastError)
|
|
309
|
+
|
|
310
|
+
if (a.hostsTried < host.length)
|
|
311
|
+
return enterBackoff(0)
|
|
312
|
+
|
|
313
|
+
options.shared.retries++
|
|
314
|
+
if (!persist) {
|
|
315
|
+
inheritedBackoff = { at: performance.now(), delay: backoffMs() }
|
|
316
|
+
return enterClosed(err)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
a.hostsTried = 0
|
|
320
|
+
enterBackoff(backoffMs())
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function detach() {
|
|
324
|
+
clearTimeout(connectTimer)
|
|
325
|
+
connectTimer = null
|
|
326
|
+
idleTimer.cancel()
|
|
327
|
+
lifeTimer.cancel()
|
|
328
|
+
incoming = Buffer.alloc(0)
|
|
329
|
+
remaining = 0
|
|
330
|
+
incomings = null
|
|
331
|
+
if (socket) {
|
|
332
|
+
socket.removeAllListeners()
|
|
333
|
+
socket.on('error', noop)
|
|
334
|
+
socket.destroy()
|
|
335
|
+
socket = null
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function enterClosed(err = Errors.connection('CONNECTION_CLOSED', options, socket)) {
|
|
340
|
+
if (phase === Phase.Closed)
|
|
341
|
+
return
|
|
342
|
+
|
|
343
|
+
const a = acquisition
|
|
344
|
+
const waiters = endWaiters
|
|
345
|
+
clearTimeout(backoffTimer)
|
|
346
|
+
backoffTimer = null
|
|
347
|
+
detach()
|
|
348
|
+
resetSocketState(err)
|
|
349
|
+
acquisition = null
|
|
350
|
+
endRequested = false
|
|
351
|
+
endWaiters = []
|
|
352
|
+
transition(Phase.Closed)
|
|
353
|
+
a && queryError(a.owner, err)
|
|
354
|
+
waiters.forEach(resolve => resolve())
|
|
355
|
+
onclose(connection, err)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function closing(quit) {
|
|
359
|
+
transition(Phase.Closing)
|
|
360
|
+
idleTimer.cancel()
|
|
361
|
+
lifeTimer.cancel()
|
|
362
|
+
quit && socket.readyState === 'open'
|
|
363
|
+
? socket.end(b().X().end())
|
|
364
|
+
: socket.destroy()
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function handoff() {
|
|
368
|
+
const owner = acquisition.owner
|
|
369
|
+
transition(Phase.Ready)
|
|
370
|
+
clearTimeout(connectTimer)
|
|
371
|
+
connectTimer = null
|
|
372
|
+
acquisition = null
|
|
373
|
+
options.shared.retries = 0
|
|
374
|
+
|
|
375
|
+
if (endRequested) {
|
|
376
|
+
endRequested = false
|
|
377
|
+
if (owner.reserve)
|
|
378
|
+
return (queryError(owner, Errors.connection('CONNECTION_ENDED', options)), closing(true))
|
|
379
|
+
if (owner.cancelled)
|
|
380
|
+
return closing(true)
|
|
381
|
+
transition(Phase.Draining)
|
|
382
|
+
return execute(owner)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
owner.reserve
|
|
386
|
+
? onopen(connection, owner)
|
|
387
|
+
: owner.cancelled
|
|
388
|
+
? onopen(connection)
|
|
389
|
+
: execute(owner)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function cancel({ pid, secret }, resolve, reject) {
|
|
393
|
+
let timeout = null
|
|
394
|
+
try {
|
|
395
|
+
let s = await Promise.resolve(options.socket ? options.socket(options) : new net.Socket())
|
|
396
|
+
const request = b().i32(16).i32(80877102).i32(pid).i32(secret).end(16)
|
|
397
|
+
const watch = x => {
|
|
398
|
+
x.once('error', reject)
|
|
399
|
+
x.on('error', noop)
|
|
400
|
+
x.once('close', () => (clearTimeout(timeout), resolve()))
|
|
401
|
+
}
|
|
402
|
+
const upgrade = () => {
|
|
403
|
+
const raw = s
|
|
404
|
+
const config = tlsConfig(raw)
|
|
405
|
+
raw.removeAllListeners()
|
|
406
|
+
s = tls.connect(config)
|
|
407
|
+
watch(s)
|
|
408
|
+
s.once('secureConnect', () => s.write(request))
|
|
409
|
+
}
|
|
410
|
+
const start = !ssl
|
|
411
|
+
? () => s.write(request)
|
|
412
|
+
: sslnegotiation === 'direct'
|
|
413
|
+
? upgrade
|
|
414
|
+
: () => {
|
|
415
|
+
s.once('data', x => x[0] === 83 || ssl !== 'prefer' ? upgrade() : s.write(request))
|
|
416
|
+
s.write(SSLRequest)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
watch(s)
|
|
420
|
+
options.connect_timeout && (timeout = setTimeout(() => s.destroy(), options.connect_timeout * 1000))
|
|
421
|
+
|
|
422
|
+
if (options.socket)
|
|
423
|
+
return start()
|
|
424
|
+
|
|
425
|
+
s.once('connect', start)
|
|
426
|
+
|
|
427
|
+
if (options.path)
|
|
428
|
+
return s.connect(options.path)
|
|
429
|
+
|
|
430
|
+
s.ssl = ssl
|
|
431
|
+
s.connect(port[0], host[0])
|
|
432
|
+
s.host = host[0]
|
|
433
|
+
s.port = port[0]
|
|
434
|
+
} catch (error) {
|
|
435
|
+
reject(error)
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function execute(q) {
|
|
440
|
+
if (phase !== Phase.Ready && phase !== Phase.Draining && !(phase === Phase.Initializing && q.initialization))
|
|
441
|
+
return queryError(q, Errors.connection('CONNECTION_CLOSED', options))
|
|
442
|
+
|
|
443
|
+
if (stream)
|
|
444
|
+
return queryError(q, Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy'))
|
|
445
|
+
|
|
446
|
+
if (q.cancelled)
|
|
447
|
+
return
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
q.state = backend
|
|
451
|
+
query
|
|
452
|
+
? sent.push(q)
|
|
453
|
+
: (query = q, query.active = true)
|
|
454
|
+
|
|
455
|
+
build(q)
|
|
456
|
+
return write(toBuffer(q))
|
|
457
|
+
&& !q.describeFirst
|
|
458
|
+
&& !q.cursorFn
|
|
459
|
+
&& sent.length < max_pipeline
|
|
460
|
+
} catch (error) {
|
|
461
|
+
sent.length === 0 && write(Sync)
|
|
462
|
+
errored(error)
|
|
463
|
+
return true
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function toBuffer(q) {
|
|
468
|
+
if (q.parameters.length >= 65534)
|
|
469
|
+
throw Errors.generic('MAX_PARAMETERS_EXCEEDED', 'Max number of parameters (65534) exceeded')
|
|
470
|
+
|
|
471
|
+
return q.options.simple
|
|
472
|
+
? b().Q().str(q.statement.string + b.N).end()
|
|
473
|
+
: q.describeFirst
|
|
474
|
+
? Buffer.concat([describe(q), Flush])
|
|
475
|
+
: q.prepare
|
|
476
|
+
? q.prepared
|
|
477
|
+
? prepared(q)
|
|
478
|
+
: Buffer.concat([describe(q), prepared(q)])
|
|
479
|
+
: unnamed(q)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function describe(q) {
|
|
483
|
+
return Buffer.concat([
|
|
484
|
+
Parse(q.statement.string, q.parameters, q.statement.types, q.statement.name),
|
|
485
|
+
Describe('S', q.statement.name)
|
|
486
|
+
])
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function prepared(q) {
|
|
490
|
+
return Buffer.concat([
|
|
491
|
+
Bind(q.parameters, q.statement.types, q.statement.name, q.cursorName),
|
|
492
|
+
q.cursorFn
|
|
493
|
+
? Execute('', q.cursorRows)
|
|
494
|
+
: ExecuteUnnamed
|
|
495
|
+
])
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function unnamed(q) {
|
|
499
|
+
return Buffer.concat([
|
|
500
|
+
Parse(q.statement.string, q.parameters, q.statement.types),
|
|
501
|
+
DescribeUnnamed,
|
|
502
|
+
prepared(q)
|
|
503
|
+
])
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function build(q) {
|
|
507
|
+
const parameters = []
|
|
508
|
+
, types = []
|
|
509
|
+
|
|
510
|
+
const string = stringify(q, q.strings[0], q.args[0], parameters, types, options)
|
|
511
|
+
|
|
512
|
+
!q.tagged && q.args.forEach(x => handleValue(x, parameters, types, options))
|
|
513
|
+
|
|
514
|
+
q.prepare = options.prepare && ('prepare' in q.options ? q.options.prepare : true)
|
|
515
|
+
q.string = string
|
|
516
|
+
q.signature = q.prepare && types + string
|
|
517
|
+
q.onlyDescribe && (delete statements[q.signature])
|
|
518
|
+
q.parameters = q.parameters || parameters
|
|
519
|
+
q.prepared = q.prepare && q.signature in statements
|
|
520
|
+
q.describeFirst = q.onlyDescribe || (parameters.length && !q.prepared)
|
|
521
|
+
q.statement = q.prepared
|
|
522
|
+
? statements[q.signature]
|
|
523
|
+
: { string, types, name: q.prepare ? statementId + statementCount++ : '' }
|
|
524
|
+
|
|
525
|
+
typeof options.debug === 'function' && options.debug(id, string, parameters, types)
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function write(x, fn) {
|
|
529
|
+
chunk = chunk ? Buffer.concat([chunk, x]) : Buffer.from(x)
|
|
530
|
+
if (fn || chunk.length >= 1024)
|
|
531
|
+
return nextWrite(fn)
|
|
532
|
+
nextWriteTimer === null && (nextWriteTimer = setImmediate(nextWrite))
|
|
533
|
+
return true
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function nextWrite(fn) {
|
|
537
|
+
const x = socket ? socket.write(chunk, fn) : false
|
|
538
|
+
nextWriteTimer !== null && clearImmediate(nextWriteTimer)
|
|
539
|
+
chunk = nextWriteTimer = null
|
|
540
|
+
return x
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function resetSocketState(err) {
|
|
544
|
+
clearImmediate(nextWriteTimer)
|
|
545
|
+
stream && (stream.destroy(err), stream = null)
|
|
546
|
+
final && (final(err), final = null)
|
|
547
|
+
query && queryError(query, err)
|
|
548
|
+
while (sent.length)
|
|
549
|
+
queryError(sent.shift(), err)
|
|
550
|
+
query = results = errorResponse = chunk = nextWriteTimer = null
|
|
551
|
+
result = new Result()
|
|
552
|
+
rows = 0
|
|
553
|
+
nonce = serverSignature = null
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* c8 ignore next 3 */
|
|
557
|
+
function drain() {
|
|
558
|
+
phase === Phase.Ready && !query && !held() && onopen(connection)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function data(x) {
|
|
562
|
+
if (incomings) {
|
|
563
|
+
incomings.push(x)
|
|
564
|
+
remaining -= x.length
|
|
565
|
+
if (remaining > 0)
|
|
566
|
+
return
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
incoming = incomings
|
|
570
|
+
? Buffer.concat(incomings, length - remaining)
|
|
571
|
+
: incoming.length === 0
|
|
572
|
+
? x
|
|
573
|
+
: Buffer.concat([incoming, x], incoming.length + x.length)
|
|
574
|
+
|
|
575
|
+
const source = socket
|
|
576
|
+
while (incoming.length > 4) {
|
|
577
|
+
length = incoming.readUInt32BE(1)
|
|
578
|
+
if (length >= incoming.length) {
|
|
579
|
+
remaining = length - incoming.length
|
|
580
|
+
incomings = [incoming]
|
|
581
|
+
break
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
handle(incoming.subarray(0, length + 1))
|
|
586
|
+
} catch (e) {
|
|
587
|
+
query && (query.cursorFn || query.describeFirst) && write(Sync)
|
|
588
|
+
starting() ? fail(e) : errored(e)
|
|
589
|
+
}
|
|
590
|
+
if (socket !== source)
|
|
591
|
+
return
|
|
592
|
+
incoming = incoming.subarray(length + 1)
|
|
593
|
+
remaining = 0
|
|
594
|
+
incomings = null
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function error(err) {
|
|
599
|
+
if (starting())
|
|
600
|
+
return fail(err)
|
|
601
|
+
|
|
602
|
+
if (phase === Phase.Ready || phase === Phase.Draining)
|
|
603
|
+
socketFailed(err)
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function socketFailed(err) {
|
|
607
|
+
errored(err)
|
|
608
|
+
while (sent.length)
|
|
609
|
+
queryError(sent.shift(), err)
|
|
610
|
+
query = null
|
|
611
|
+
options.shared.retries++
|
|
612
|
+
inheritedBackoff = { at: performance.now(), delay: backoffMs() }
|
|
613
|
+
closing(false)
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function errored(err) {
|
|
617
|
+
stream && (stream.destroy(err), stream = null)
|
|
618
|
+
query && queryError(query, err)
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function queryError(query, err) {
|
|
622
|
+
if (query.reserve)
|
|
623
|
+
return query.reject(err)
|
|
624
|
+
|
|
625
|
+
if (!err || typeof err !== 'object')
|
|
626
|
+
err = new Error(err)
|
|
627
|
+
|
|
628
|
+
'query' in err || 'parameters' in err || Object.defineProperties(err, {
|
|
629
|
+
stack: { value: err.stack + query.origin.replace(/.*\n/, '\n'), enumerable: options.debug },
|
|
630
|
+
query: { value: query.string, enumerable: options.debug },
|
|
631
|
+
parameters: { value: query.parameters, enumerable: options.debug },
|
|
632
|
+
args: { value: query.args, enumerable: options.debug },
|
|
633
|
+
types: { value: query.statement && query.statement.types, enumerable: options.debug }
|
|
634
|
+
})
|
|
635
|
+
query.reject(err)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function end() {
|
|
639
|
+
if (phase === Phase.Closed)
|
|
640
|
+
return Promise.resolve()
|
|
641
|
+
|
|
642
|
+
const done = new Promise(resolve => endWaiters.push(resolve))
|
|
643
|
+
|
|
644
|
+
if (phase === Phase.Closing || phase === Phase.Draining)
|
|
645
|
+
return done
|
|
646
|
+
|
|
647
|
+
if (phase === Phase.Ready) {
|
|
648
|
+
!held() && onend(connection)
|
|
649
|
+
idle() && !held()
|
|
650
|
+
? closing(true)
|
|
651
|
+
: transition(Phase.Draining)
|
|
652
|
+
return done
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (acquisition.owner.reserve)
|
|
656
|
+
enterClosed(Errors.connection('CONNECTION_ENDED', options))
|
|
657
|
+
else if (phase === Phase.Backoff && acquisition.attempts > 0)
|
|
658
|
+
enterClosed(acquisition.lastError)
|
|
659
|
+
else if (!endRequested)
|
|
660
|
+
(endRequested = true, onend(connection))
|
|
661
|
+
|
|
662
|
+
return done
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function terminate() {
|
|
666
|
+
if (phase === Phase.Closed)
|
|
667
|
+
return
|
|
668
|
+
|
|
669
|
+
const destroyed = Errors.connection('CONNECTION_DESTROYED', options, socket)
|
|
670
|
+
if (phase === Phase.Closing)
|
|
671
|
+
return socket.destroy()
|
|
672
|
+
|
|
673
|
+
if (phase !== Phase.Ready && phase !== Phase.Draining)
|
|
674
|
+
return enterClosed(destroyed)
|
|
675
|
+
|
|
676
|
+
const inflight = !idle()
|
|
677
|
+
resetSocketState(destroyed)
|
|
678
|
+
enterClosed(inflight ? destroyed : undefined)
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function release() {
|
|
682
|
+
phase === Phase.Ready
|
|
683
|
+
? onopen(connection)
|
|
684
|
+
: phase === Phase.Draining && idle() && drained()
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function closed(hadError) {
|
|
688
|
+
const err = Errors.connection('CONNECTION_CLOSED', options, socket)
|
|
689
|
+
if (starting())
|
|
690
|
+
return fail(errorResponse || err, true)
|
|
691
|
+
|
|
692
|
+
if (!inheritedBackoff && (phase !== Phase.Closing || hadError)) {
|
|
693
|
+
hadError && options.shared.retries++
|
|
694
|
+
inheritedBackoff = { at: performance.now(), delay: backoffMs() }
|
|
695
|
+
}
|
|
696
|
+
enterClosed(err)
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
/* Handlers */
|
|
701
|
+
function handle(xs, x = xs[0]) {
|
|
702
|
+
(
|
|
703
|
+
x === 68 ? DataRow : // D
|
|
704
|
+
x === 100 ? CopyData : // d
|
|
705
|
+
x === 65 ? NotificationResponse : // A
|
|
706
|
+
x === 83 ? ParameterStatus : // S
|
|
707
|
+
x === 90 ? ReadyForQuery : // Z
|
|
708
|
+
x === 67 ? CommandComplete : // C
|
|
709
|
+
x === 50 ? BindComplete : // 2
|
|
710
|
+
x === 49 ? ParseComplete : // 1
|
|
711
|
+
x === 116 ? ParameterDescription : // t
|
|
712
|
+
x === 84 ? RowDescription : // T
|
|
713
|
+
x === 82 ? Authentication : // R
|
|
714
|
+
x === 110 ? NoData : // n
|
|
715
|
+
x === 75 ? BackendKeyData : // K
|
|
716
|
+
x === 69 ? ErrorResponse : // E
|
|
717
|
+
x === 115 ? PortalSuspended : // s
|
|
718
|
+
x === 51 ? CloseComplete : // 3
|
|
719
|
+
x === 71 ? CopyInResponse : // G
|
|
720
|
+
x === 78 ? NoticeResponse : // N
|
|
721
|
+
x === 72 ? CopyOutResponse : // H
|
|
722
|
+
x === 99 ? CopyDone : // c
|
|
723
|
+
x === 73 ? EmptyQueryResponse : // I
|
|
724
|
+
x === 86 ? FunctionCallResponse : // V
|
|
725
|
+
x === 118 ? NegotiateProtocolVersion : // v
|
|
726
|
+
x === 87 ? CopyBothResponse : // W
|
|
727
|
+
/* c8 ignore next */
|
|
728
|
+
UnknownMessage
|
|
729
|
+
)(xs)
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function DataRow(x) {
|
|
733
|
+
let index = 7
|
|
734
|
+
let length
|
|
735
|
+
let column
|
|
736
|
+
let value
|
|
737
|
+
|
|
738
|
+
const valueFrom = !query.initialization && transform.value.from
|
|
739
|
+
const rowFrom = !query.initialization && transform.row.from
|
|
740
|
+
const row = query.isRaw ? new Array(query.statement.columns.length) : {}
|
|
741
|
+
for (let i = 0; i < query.statement.columns.length; i++) {
|
|
742
|
+
column = query.statement.columns[i]
|
|
743
|
+
length = x.readInt32BE(index)
|
|
744
|
+
index += 4
|
|
745
|
+
|
|
746
|
+
value = length === -1
|
|
747
|
+
? null
|
|
748
|
+
: query.isRaw === true
|
|
749
|
+
? x.subarray(index, index += length)
|
|
750
|
+
: column.parser === undefined
|
|
751
|
+
? x.toString('utf8', index, index += length)
|
|
752
|
+
: column.parser.array === true
|
|
753
|
+
? column.parser(x.toString('utf8', index + 1, index += length))
|
|
754
|
+
: column.parser(x.toString('utf8', index, index += length))
|
|
755
|
+
|
|
756
|
+
query.isRaw
|
|
757
|
+
? (row[i] = query.isRaw === true
|
|
758
|
+
? value
|
|
759
|
+
: valueFrom ? transform.value.from(value, column) : value)
|
|
760
|
+
: (row[column.name] = valueFrom ? transform.value.from(value, column) : value)
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
query.forEachFn
|
|
764
|
+
? query.forEachFn(rowFrom ? transform.row.from(row) : row, result)
|
|
765
|
+
: (result[rows++] = rowFrom ? transform.row.from(row) : row)
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function ParameterStatus(x) {
|
|
769
|
+
const [k, v] = x.toString('utf8', 5, x.length - 1).split(b.N)
|
|
770
|
+
backendParameters[k] = v
|
|
771
|
+
if (options.parameters[k] !== v) {
|
|
772
|
+
options.parameters[k] = v
|
|
773
|
+
onparameter && onparameter(k, v)
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function ReadyForQuery(x) {
|
|
778
|
+
if (query) {
|
|
779
|
+
if (errorResponse) {
|
|
780
|
+
if (query.initialization)
|
|
781
|
+
return enterClosed(errorResponse)
|
|
782
|
+
query.retried
|
|
783
|
+
? errored(query.retried)
|
|
784
|
+
: query.prepared && retryRoutines.has(errorResponse.routine)
|
|
785
|
+
? retry(query, errorResponse)
|
|
786
|
+
: errored(errorResponse)
|
|
787
|
+
} else {
|
|
788
|
+
query.initialization && query.initialization(results || result)
|
|
789
|
+
query.resolve(results || result)
|
|
790
|
+
}
|
|
791
|
+
} else if (errorResponse) {
|
|
792
|
+
errored(errorResponse)
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
query = results = errorResponse = null
|
|
796
|
+
result = new Result()
|
|
797
|
+
|
|
798
|
+
if (phase === Phase.Authenticating || phase === Phase.Initializing)
|
|
799
|
+
return initialized()
|
|
800
|
+
|
|
801
|
+
if (phase === Phase.Closing)
|
|
802
|
+
return
|
|
803
|
+
|
|
804
|
+
while (sent.length && (query = sent.shift()) && (query.active = true, query.cancelled))
|
|
805
|
+
Connection(options).cancel(query.state, query.cancelled.resolve, query.cancelled.reject)
|
|
806
|
+
|
|
807
|
+
if (query)
|
|
808
|
+
return
|
|
809
|
+
|
|
810
|
+
connection.reserved
|
|
811
|
+
? !connection.reserved.release && x[5] === 73
|
|
812
|
+
? phase === Phase.Draining
|
|
813
|
+
? drained()
|
|
814
|
+
: (connection.reserved = null, onopen(connection))
|
|
815
|
+
: connection.reserved()
|
|
816
|
+
: phase === Phase.Draining
|
|
817
|
+
? drained()
|
|
818
|
+
: onopen(connection)
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function initialized() {
|
|
822
|
+
if (target_session_attrs) {
|
|
823
|
+
if (!backendParameters.in_hot_standby || !backendParameters.default_transaction_read_only)
|
|
824
|
+
return fetchState()
|
|
825
|
+
if (tryNext(target_session_attrs, backendParameters)) {
|
|
826
|
+
acquisition.mismatch = true
|
|
827
|
+
return fail(Errors.connection('CONNECTION_CLOSED', options, socket), true)
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (needsTypes)
|
|
832
|
+
return fetchArrayTypes()
|
|
833
|
+
|
|
834
|
+
handoff()
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
function CommandComplete(x) {
|
|
838
|
+
rows = 0
|
|
839
|
+
|
|
840
|
+
for (let i = x.length - 1; i > 0; i--) {
|
|
841
|
+
if (x[i] === 32 && x[i + 1] < 58 && result.count === null)
|
|
842
|
+
result.count = +x.toString('utf8', i + 1, x.length - 1)
|
|
843
|
+
if (x[i - 1] >= 65) {
|
|
844
|
+
result.command = x.toString('utf8', 5, i)
|
|
845
|
+
result.state = backend
|
|
846
|
+
break
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
final && (final(), final = null)
|
|
851
|
+
|
|
852
|
+
if (result.command === 'BEGIN' && max !== 1 && !connection.reserved)
|
|
853
|
+
return errored(Errors.generic('UNSAFE_TRANSACTION', 'Only use sql.begin, sql.reserved or max: 1'))
|
|
854
|
+
|
|
855
|
+
if (query.options.simple)
|
|
856
|
+
return BindComplete()
|
|
857
|
+
|
|
858
|
+
if (query.cursorFn) {
|
|
859
|
+
result.count && query.cursorFn(result)
|
|
860
|
+
write(Sync)
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function ParseComplete() {
|
|
865
|
+
query.parsing = false
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function BindComplete() {
|
|
869
|
+
!result.statement && (result.statement = query.statement)
|
|
870
|
+
result.columns = query.statement.columns
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function ParameterDescription(x) {
|
|
874
|
+
const length = x.readUInt16BE(5)
|
|
875
|
+
|
|
876
|
+
for (let i = 0; i < length; ++i)
|
|
877
|
+
!query.statement.types[i] && (query.statement.types[i] = x.readUInt32BE(7 + i * 4))
|
|
878
|
+
|
|
879
|
+
query.prepare && (statements[query.signature] = query.statement)
|
|
880
|
+
query.describeFirst && !query.onlyDescribe && (write(prepared(query)), query.describeFirst = false)
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function RowDescription(x) {
|
|
884
|
+
if (result.command) {
|
|
885
|
+
results = results || [result]
|
|
886
|
+
results.push(result = new Result())
|
|
887
|
+
result.count = null
|
|
888
|
+
query.statement.columns = null
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
const length = x.readUInt16BE(5)
|
|
892
|
+
let index = 7
|
|
893
|
+
let start
|
|
894
|
+
|
|
895
|
+
query.statement.columns = Array(length)
|
|
896
|
+
|
|
897
|
+
for (let i = 0; i < length; ++i) {
|
|
898
|
+
start = index
|
|
899
|
+
while (x[index++] !== 0);
|
|
900
|
+
const table = x.readUInt32BE(index)
|
|
901
|
+
const number = x.readUInt16BE(index + 4)
|
|
902
|
+
const type = x.readUInt32BE(index + 6)
|
|
903
|
+
query.statement.columns[i] = {
|
|
904
|
+
name: !query.initialization && transform.column.from
|
|
905
|
+
? transform.column.from(x.toString('utf8', start, index - 1))
|
|
906
|
+
: x.toString('utf8', start, index - 1),
|
|
907
|
+
parser: parsers[type],
|
|
908
|
+
table,
|
|
909
|
+
number,
|
|
910
|
+
type
|
|
911
|
+
}
|
|
912
|
+
index += 18
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
result.statement = query.statement
|
|
916
|
+
if (query.onlyDescribe)
|
|
917
|
+
return (query.resolve(query.statement), write(Sync))
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function Authentication(x, type = x.readUInt32BE(5)) {
|
|
921
|
+
const attempt = generation
|
|
922
|
+
Promise.resolve((
|
|
923
|
+
type === 3 ? AuthenticationCleartextPassword :
|
|
924
|
+
type === 5 ? AuthenticationMD5Password :
|
|
925
|
+
type === 10 ? SASL :
|
|
926
|
+
type === 11 ? SASLContinue :
|
|
927
|
+
type === 12 ? SASLFinal :
|
|
928
|
+
type !== 0 ? UnknownAuth :
|
|
929
|
+
noop
|
|
930
|
+
)(x, type, attempt)).catch(err => attempt === generation && enterClosed(err))
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/* c8 ignore next 5 */
|
|
934
|
+
async function AuthenticationCleartextPassword(x, type, attempt) {
|
|
935
|
+
const payload = await Pass()
|
|
936
|
+
if (attempt !== generation)
|
|
937
|
+
return
|
|
938
|
+
write(
|
|
939
|
+
b().p().str(payload).z(1).end()
|
|
940
|
+
)
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
async function AuthenticationMD5Password(x, type, attempt) {
|
|
944
|
+
const payload = 'md5' + (
|
|
945
|
+
await md5(
|
|
946
|
+
Buffer.concat([
|
|
947
|
+
Buffer.from(await md5((await Pass()) + user)),
|
|
948
|
+
x.subarray(9)
|
|
949
|
+
])
|
|
950
|
+
)
|
|
951
|
+
)
|
|
952
|
+
if (attempt !== generation)
|
|
953
|
+
return
|
|
954
|
+
write(
|
|
955
|
+
b().p().str(payload).z(1).end()
|
|
956
|
+
)
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
async function SASL(x, type, attempt) {
|
|
960
|
+
const nextNonce = (await crypto.randomBytes(18)).toString('base64')
|
|
961
|
+
if (attempt !== generation)
|
|
962
|
+
return
|
|
963
|
+
nonce = nextNonce
|
|
964
|
+
b().p().str('SCRAM-SHA-256' + b.N)
|
|
965
|
+
const i = b.i
|
|
966
|
+
write(b.inc(4).str('n,,n=*,r=' + nonce).i32(b.i - i - 4, i).end())
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
async function SASLContinue(x, type, attempt) {
|
|
970
|
+
const clientNonce = nonce
|
|
971
|
+
const res = x.toString('utf8', 9).split(',').reduce((acc, x) => (acc[x[0]] = x.slice(2), acc), {})
|
|
972
|
+
|
|
973
|
+
const saltedPassword = await crypto.pbkdf2Sync(
|
|
974
|
+
await Pass(),
|
|
975
|
+
Buffer.from(res.s, 'base64'),
|
|
976
|
+
parseInt(res.i), 32,
|
|
977
|
+
'sha256'
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
const clientKey = await hmac(saltedPassword, 'Client Key')
|
|
981
|
+
|
|
982
|
+
const auth = 'n=*,r=' + clientNonce + ','
|
|
983
|
+
+ 'r=' + res.r + ',s=' + res.s + ',i=' + res.i
|
|
984
|
+
+ ',c=biws,r=' + res.r
|
|
985
|
+
|
|
986
|
+
const signature = (await hmac(await hmac(saltedPassword, 'Server Key'), auth)).toString('base64')
|
|
987
|
+
|
|
988
|
+
const payload = 'c=biws,r=' + res.r + ',p=' + xor(
|
|
989
|
+
clientKey, Buffer.from(await hmac(await sha256(clientKey), auth))
|
|
990
|
+
).toString('base64')
|
|
991
|
+
|
|
992
|
+
if (attempt !== generation)
|
|
993
|
+
return
|
|
994
|
+
serverSignature = signature
|
|
995
|
+
write(
|
|
996
|
+
b().p().str(payload).end()
|
|
997
|
+
)
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
function SASLFinal(x) {
|
|
1001
|
+
if (x.toString('utf8', 9).split(b.N, 1)[0].slice(2) === serverSignature)
|
|
1002
|
+
return
|
|
1003
|
+
/* c8 ignore next 5 */
|
|
1004
|
+
enterClosed(Errors.generic('SASL_SIGNATURE_MISMATCH', 'The server did not return the correct signature'))
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function Pass() {
|
|
1008
|
+
return Promise.resolve(typeof options.pass === 'function'
|
|
1009
|
+
? options.pass()
|
|
1010
|
+
: options.pass
|
|
1011
|
+
)
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
function NoData() {
|
|
1015
|
+
result.statement = query.statement
|
|
1016
|
+
result.statement.columns = []
|
|
1017
|
+
if (query.onlyDescribe)
|
|
1018
|
+
return (query.resolve(query.statement), write(Sync))
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function BackendKeyData(x) {
|
|
1022
|
+
backend.pid = x.readUInt32BE(5)
|
|
1023
|
+
backend.secret = x.readUInt32BE(9)
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
function fetchArrayTypes() {
|
|
1027
|
+
needsTypes = false
|
|
1028
|
+
initialize(`
|
|
1029
|
+
select b.oid, b.typarray
|
|
1030
|
+
from pg_catalog.pg_type a
|
|
1031
|
+
left join pg_catalog.pg_type b on b.oid = a.typelem
|
|
1032
|
+
where a.typcategory = 'A'
|
|
1033
|
+
group by b.oid, b.typarray
|
|
1034
|
+
order by b.oid
|
|
1035
|
+
`, types => types.forEach(({ oid, typarray }) => addArrayType(options, oid, typarray)))
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
function tryNext(x, xs) {
|
|
1039
|
+
return (
|
|
1040
|
+
(x === 'read-write' && xs.default_transaction_read_only === 'on') ||
|
|
1041
|
+
(x === 'read-only' && xs.default_transaction_read_only === 'off') ||
|
|
1042
|
+
(x === 'primary' && xs.in_hot_standby === 'on') ||
|
|
1043
|
+
(x === 'standby' && xs.in_hot_standby === 'off') ||
|
|
1044
|
+
(x === 'prefer-standby' && xs.in_hot_standby === 'off' && host.length > 1 && !acquisition.mismatch)
|
|
1045
|
+
)
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function fetchState() {
|
|
1049
|
+
initialize(`
|
|
1050
|
+
show transaction_read_only;
|
|
1051
|
+
select pg_catalog.pg_is_in_recovery()
|
|
1052
|
+
`, ([[a], [b]]) => {
|
|
1053
|
+
backendParameters.default_transaction_read_only = a.transaction_read_only
|
|
1054
|
+
backendParameters.in_hot_standby = b.pg_is_in_recovery ? 'on' : 'off'
|
|
1055
|
+
}, true)
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function initialize(string, resolve, simple = false) {
|
|
1059
|
+
transition(Phase.Initializing)
|
|
1060
|
+
const attempt = generation
|
|
1061
|
+
const q = new Query([string], [], q => attempt === generation
|
|
1062
|
+
? execute(q)
|
|
1063
|
+
: queryError(q, Errors.connection('CONNECTION_CLOSED', options)), null, { simple })
|
|
1064
|
+
q.initialization = resolve
|
|
1065
|
+
q.catch(noop)
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
function ErrorResponse(x) {
|
|
1069
|
+
if (query) {
|
|
1070
|
+
(query.cursorFn || query.describeFirst) && write(Sync)
|
|
1071
|
+
errorResponse = Errors.postgres(parseError(x))
|
|
1072
|
+
} else {
|
|
1073
|
+
const err = Errors.postgres(parseError(x))
|
|
1074
|
+
phase === Phase.Ready || phase === Phase.Draining
|
|
1075
|
+
? socketFailed(err)
|
|
1076
|
+
: phase === Phase.Authenticating && fail(err)
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function retry(q, error) {
|
|
1081
|
+
delete statements[q.signature]
|
|
1082
|
+
q.retried = error
|
|
1083
|
+
execute(q)
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
function NotificationResponse(x) {
|
|
1087
|
+
if (!onnotify)
|
|
1088
|
+
return
|
|
1089
|
+
|
|
1090
|
+
let index = 9
|
|
1091
|
+
while (x[index++] !== 0);
|
|
1092
|
+
onnotify(
|
|
1093
|
+
x.toString('utf8', 9, index - 1),
|
|
1094
|
+
x.toString('utf8', index, x.length - 1)
|
|
1095
|
+
)
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
async function PortalSuspended() {
|
|
1099
|
+
const current = query
|
|
1100
|
+
const source = socket
|
|
1101
|
+
try {
|
|
1102
|
+
const x = await Promise.resolve(current.cursorFn(result))
|
|
1103
|
+
if (query !== current || socket !== source)
|
|
1104
|
+
return
|
|
1105
|
+
rows = 0
|
|
1106
|
+
x === CLOSE
|
|
1107
|
+
? write(Close(current.portal))
|
|
1108
|
+
: (result = new Result(), write(Execute('', current.cursorRows)))
|
|
1109
|
+
} catch (err) {
|
|
1110
|
+
query === current && socket === source && write(Sync)
|
|
1111
|
+
current.reject(err)
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
function CloseComplete() {
|
|
1116
|
+
result.count && query.cursorFn(result)
|
|
1117
|
+
query.resolve(result)
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function CopyInResponse() {
|
|
1121
|
+
stream = new Stream.Writable({
|
|
1122
|
+
autoDestroy: true,
|
|
1123
|
+
write(chunk, encoding, callback) {
|
|
1124
|
+
socket.write(b().d().raw(chunk).end(), callback)
|
|
1125
|
+
},
|
|
1126
|
+
destroy(error, callback) {
|
|
1127
|
+
callback(error)
|
|
1128
|
+
socket && socket.readyState === 'open' && socket.write(b().f().str(error + b.N).end())
|
|
1129
|
+
stream = null
|
|
1130
|
+
},
|
|
1131
|
+
final(callback) {
|
|
1132
|
+
socket.write(b().c().end())
|
|
1133
|
+
final = callback
|
|
1134
|
+
stream = null
|
|
1135
|
+
}
|
|
1136
|
+
})
|
|
1137
|
+
query.resolve(stream)
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
function CopyOutResponse() {
|
|
1141
|
+
stream = new Stream.Readable({
|
|
1142
|
+
read() { socket.resume() }
|
|
1143
|
+
})
|
|
1144
|
+
query.resolve(stream)
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/* c8 ignore next 3 */
|
|
1148
|
+
function CopyBothResponse() {
|
|
1149
|
+
stream = new Stream.Duplex({
|
|
1150
|
+
autoDestroy: true,
|
|
1151
|
+
read() { socket.resume() },
|
|
1152
|
+
/* c8 ignore next 11 */
|
|
1153
|
+
write(chunk, encoding, callback) {
|
|
1154
|
+
socket.write(b().d().raw(chunk).end(), callback)
|
|
1155
|
+
},
|
|
1156
|
+
destroy(error, callback) {
|
|
1157
|
+
callback(error)
|
|
1158
|
+
socket && socket.readyState === 'open' && socket.write(b().f().str(error + b.N).end())
|
|
1159
|
+
stream = null
|
|
1160
|
+
},
|
|
1161
|
+
final(callback) {
|
|
1162
|
+
socket.write(b().c().end())
|
|
1163
|
+
final = callback
|
|
1164
|
+
}
|
|
1165
|
+
})
|
|
1166
|
+
query.resolve(stream)
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function CopyData(x) {
|
|
1170
|
+
stream && (stream.push(x.subarray(5)) || socket.pause())
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function CopyDone() {
|
|
1174
|
+
stream && stream.push(null)
|
|
1175
|
+
stream = null
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function NoticeResponse(x) {
|
|
1179
|
+
onnotice
|
|
1180
|
+
? onnotice(parseError(x))
|
|
1181
|
+
: console.log(parseError(x)) // eslint-disable-line
|
|
1182
|
+
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
/* c8 ignore next 3 */
|
|
1186
|
+
function EmptyQueryResponse() {
|
|
1187
|
+
/* noop */
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/* c8 ignore next 3 */
|
|
1191
|
+
function FunctionCallResponse() {
|
|
1192
|
+
errored(Errors.notSupported('FunctionCallResponse'))
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/* c8 ignore next 3 */
|
|
1196
|
+
function NegotiateProtocolVersion() {
|
|
1197
|
+
errored(Errors.notSupported('NegotiateProtocolVersion'))
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/* c8 ignore next 3 */
|
|
1201
|
+
function UnknownMessage(x) {
|
|
1202
|
+
console.error('Postgres.js : Unknown Message:', x[0]) // eslint-disable-line
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/* c8 ignore next 3 */
|
|
1206
|
+
function UnknownAuth(x, type) {
|
|
1207
|
+
console.error('Postgres.js : Unknown Auth:', type) // eslint-disable-line
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
/* Messages */
|
|
1211
|
+
function Bind(parameters, types, statement = '', portal = '') {
|
|
1212
|
+
let prev
|
|
1213
|
+
, type
|
|
1214
|
+
|
|
1215
|
+
b().B().str(portal + b.N).str(statement + b.N).i16(0).i16(parameters.length)
|
|
1216
|
+
|
|
1217
|
+
parameters.forEach((x, i) => {
|
|
1218
|
+
if (x === null)
|
|
1219
|
+
return b.i32(0xFFFFFFFF)
|
|
1220
|
+
|
|
1221
|
+
type = types[i]
|
|
1222
|
+
x = type in options.serializers
|
|
1223
|
+
? options.serializers[type](x)
|
|
1224
|
+
: '' + x
|
|
1225
|
+
|
|
1226
|
+
prev = b.i
|
|
1227
|
+
b.inc(4).str(x).i32(b.i - prev - 4, prev)
|
|
1228
|
+
})
|
|
1229
|
+
|
|
1230
|
+
b.i16(0)
|
|
1231
|
+
|
|
1232
|
+
return b.end()
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
function Parse(str, parameters, types, name = '') {
|
|
1236
|
+
b().P().str(name + b.N).str(str + b.N).i16(parameters.length)
|
|
1237
|
+
parameters.forEach((x, i) => b.i32(types[i] || 0))
|
|
1238
|
+
return b.end()
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
function Describe(x, name = '') {
|
|
1242
|
+
return b().D().str(x).str(name + b.N).end()
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
function Execute(portal = '', rows = 0) {
|
|
1246
|
+
return Buffer.concat([
|
|
1247
|
+
b().E().str(portal + b.N).i32(rows).end(),
|
|
1248
|
+
Flush
|
|
1249
|
+
])
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
function Close(portal = '') {
|
|
1253
|
+
return Buffer.concat([
|
|
1254
|
+
b().C().str('P').str(portal + b.N).end(),
|
|
1255
|
+
b().S().end()
|
|
1256
|
+
])
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
function StartupMessage() {
|
|
1260
|
+
return b().inc(4).i16(3).z(2).str(
|
|
1261
|
+
Object.entries(Object.assign({
|
|
1262
|
+
user,
|
|
1263
|
+
database,
|
|
1264
|
+
client_encoding: 'UTF8'
|
|
1265
|
+
},
|
|
1266
|
+
options.connection
|
|
1267
|
+
)).filter(([, v]) => v).map(([k, v]) => k + b.N + v).join(b.N)
|
|
1268
|
+
).z(2).end(0)
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
function parseError(x) {
|
|
1274
|
+
const error = {}
|
|
1275
|
+
let start = 5
|
|
1276
|
+
for (let i = 5; i < x.length - 1; i++) {
|
|
1277
|
+
if (x[i] === 0) {
|
|
1278
|
+
error[errorFields[x[start]]] = x.toString('utf8', start + 1, i)
|
|
1279
|
+
start = i + 1
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return error
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
function md5(x) {
|
|
1286
|
+
return crypto.createHash('md5').update(x).digest('hex')
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
function hmac(key, x) {
|
|
1290
|
+
return crypto.createHmac('sha256', key).update(x).digest()
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
function sha256(x) {
|
|
1294
|
+
return crypto.createHash('sha256').update(x).digest()
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
function xor(a, b) {
|
|
1298
|
+
const length = Math.max(a.length, b.length)
|
|
1299
|
+
const buffer = Buffer.allocUnsafe(length)
|
|
1300
|
+
for (let i = 0; i < length; i++)
|
|
1301
|
+
buffer[i] = a[i] ^ b[i]
|
|
1302
|
+
return buffer
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
function timer(fn, seconds) {
|
|
1306
|
+
seconds = typeof seconds === 'function' ? seconds() : seconds
|
|
1307
|
+
if (!seconds)
|
|
1308
|
+
return { cancel: noop, start: noop }
|
|
1309
|
+
|
|
1310
|
+
let timer
|
|
1311
|
+
return {
|
|
1312
|
+
cancel() {
|
|
1313
|
+
timer && (clearTimeout(timer), timer = null)
|
|
1314
|
+
},
|
|
1315
|
+
start(...args) {
|
|
1316
|
+
timer && clearTimeout(timer)
|
|
1317
|
+
timer = setTimeout(() => done(args), seconds * 1000)
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
function done(args) {
|
|
1322
|
+
timer = null
|
|
1323
|
+
fn.apply(null, args)
|
|
1324
|
+
}
|
|
1325
|
+
}
|