@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
package/src/errors.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export class PostgresError extends Error {
|
|
2
|
+
constructor(x) {
|
|
3
|
+
super(x.message)
|
|
4
|
+
this.name = this.constructor.name
|
|
5
|
+
Object.assign(this, x)
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Errors = {
|
|
10
|
+
connection,
|
|
11
|
+
postgres,
|
|
12
|
+
generic,
|
|
13
|
+
notSupported
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function connection(x, options, socket) {
|
|
17
|
+
const { host, port } = socket || options
|
|
18
|
+
const error = Object.assign(
|
|
19
|
+
new Error(('write ' + x + ' ' + (options.path || (host + ':' + port)))),
|
|
20
|
+
{
|
|
21
|
+
code: x,
|
|
22
|
+
errno: x,
|
|
23
|
+
address: options.path || host
|
|
24
|
+
}, options.path ? {} : { port: port }
|
|
25
|
+
)
|
|
26
|
+
Error.captureStackTrace(error, connection)
|
|
27
|
+
return error
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function postgres(x) {
|
|
31
|
+
const error = new PostgresError(x)
|
|
32
|
+
Error.captureStackTrace(error, postgres)
|
|
33
|
+
return error
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function generic(code, message) {
|
|
37
|
+
const error = Object.assign(new Error(code + ': ' + message), { code })
|
|
38
|
+
Error.captureStackTrace(error, generic)
|
|
39
|
+
return error
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* c8 ignore next 10 */
|
|
43
|
+
function notSupported(x) {
|
|
44
|
+
const error = Object.assign(
|
|
45
|
+
new Error(x + ' (B) is not supported'),
|
|
46
|
+
{
|
|
47
|
+
code: 'MESSAGE_NOT_SUPPORTED',
|
|
48
|
+
name: x
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
Error.captureStackTrace(error, notSupported)
|
|
52
|
+
return error
|
|
53
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
import os from 'os'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
mergeUserTypes,
|
|
6
|
+
addDefaultArrayTypes,
|
|
7
|
+
inferType,
|
|
8
|
+
Parameter,
|
|
9
|
+
Identifier,
|
|
10
|
+
Builder,
|
|
11
|
+
toPascal,
|
|
12
|
+
pascal,
|
|
13
|
+
toCamel,
|
|
14
|
+
camel,
|
|
15
|
+
toKebab,
|
|
16
|
+
kebab,
|
|
17
|
+
fromPascal,
|
|
18
|
+
fromCamel,
|
|
19
|
+
fromKebab
|
|
20
|
+
} from './types.js'
|
|
21
|
+
|
|
22
|
+
import Connection from './connection.js'
|
|
23
|
+
import { Query, CLOSE } from './query.js'
|
|
24
|
+
import Queue from './queue.js'
|
|
25
|
+
import { Errors, PostgresError } from './errors.js'
|
|
26
|
+
import Subscribe from './subscribe.js'
|
|
27
|
+
import largeObject from './large.js'
|
|
28
|
+
|
|
29
|
+
Object.assign(Postgres, {
|
|
30
|
+
PostgresError,
|
|
31
|
+
toPascal,
|
|
32
|
+
pascal,
|
|
33
|
+
toCamel,
|
|
34
|
+
camel,
|
|
35
|
+
toKebab,
|
|
36
|
+
kebab,
|
|
37
|
+
fromPascal,
|
|
38
|
+
fromCamel,
|
|
39
|
+
fromKebab,
|
|
40
|
+
BigInt: {
|
|
41
|
+
to: 20,
|
|
42
|
+
from: [20],
|
|
43
|
+
parse: x => BigInt(x), // eslint-disable-line
|
|
44
|
+
serialize: x => x.toString()
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
export default Postgres
|
|
49
|
+
|
|
50
|
+
function Postgres(a, b) {
|
|
51
|
+
const options = parseOptions(a, b)
|
|
52
|
+
, subscribe = options.no_subscribe || Subscribe(Postgres, { ...options })
|
|
53
|
+
|
|
54
|
+
let ending = false
|
|
55
|
+
|
|
56
|
+
const queries = Queue()
|
|
57
|
+
, connecting = Queue()
|
|
58
|
+
, reserved = Queue()
|
|
59
|
+
, closed = Queue()
|
|
60
|
+
, ended = Queue()
|
|
61
|
+
, open = Queue()
|
|
62
|
+
, busy = Queue()
|
|
63
|
+
, full = Queue()
|
|
64
|
+
, queues = { connecting, reserved, closed, ended, open, busy, full }
|
|
65
|
+
|
|
66
|
+
const connections = [...Array(options.max)].map(() => Connection(options, queues, { onopen, onend, onclose, ondrain }))
|
|
67
|
+
|
|
68
|
+
const sql = Sql(handler)
|
|
69
|
+
|
|
70
|
+
Object.assign(sql, {
|
|
71
|
+
get parameters() { return options.parameters },
|
|
72
|
+
largeObject: largeObject.bind(null, sql),
|
|
73
|
+
subscribe,
|
|
74
|
+
CLOSE,
|
|
75
|
+
END: CLOSE,
|
|
76
|
+
PostgresError,
|
|
77
|
+
options,
|
|
78
|
+
reserve,
|
|
79
|
+
listen,
|
|
80
|
+
begin,
|
|
81
|
+
close,
|
|
82
|
+
end
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
return sql
|
|
86
|
+
|
|
87
|
+
function Sql(handler) {
|
|
88
|
+
handler.debug = options.debug
|
|
89
|
+
|
|
90
|
+
Object.entries(options.types).reduce((acc, [name, type]) => {
|
|
91
|
+
acc[name] = (x) => new Parameter(x, type.to)
|
|
92
|
+
return acc
|
|
93
|
+
}, typed)
|
|
94
|
+
|
|
95
|
+
Object.assign(sql, {
|
|
96
|
+
types: typed,
|
|
97
|
+
typed,
|
|
98
|
+
unsafe,
|
|
99
|
+
notify,
|
|
100
|
+
array,
|
|
101
|
+
json,
|
|
102
|
+
file
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
return sql
|
|
106
|
+
|
|
107
|
+
function typed(value, type) {
|
|
108
|
+
return new Parameter(value, type)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function sql(strings, ...args) {
|
|
112
|
+
const query = strings && Array.isArray(strings.raw)
|
|
113
|
+
? new Query(strings, args, handler, cancel)
|
|
114
|
+
: typeof strings === 'string' && !args.length
|
|
115
|
+
? new Identifier(options.transform.column.to ? options.transform.column.to(strings) : strings)
|
|
116
|
+
: new Builder(strings, args)
|
|
117
|
+
return query
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function unsafe(string, args = [], options = {}) {
|
|
121
|
+
arguments.length === 2 && !Array.isArray(args) && (options = args, args = [])
|
|
122
|
+
const query = new Query([string], args, handler, cancel, {
|
|
123
|
+
prepare: false,
|
|
124
|
+
...options,
|
|
125
|
+
simple: 'simple' in options ? options.simple : args.length === 0
|
|
126
|
+
})
|
|
127
|
+
return query
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function file(path, args = [], options = {}) {
|
|
131
|
+
arguments.length === 2 && !Array.isArray(args) && (options = args, args = [])
|
|
132
|
+
const query = new Query([], args, (query) => {
|
|
133
|
+
fs.readFile(path, 'utf8', (err, string) => {
|
|
134
|
+
if (err)
|
|
135
|
+
return query.reject(err)
|
|
136
|
+
|
|
137
|
+
query.strings = [string]
|
|
138
|
+
handler(query)
|
|
139
|
+
})
|
|
140
|
+
}, cancel, {
|
|
141
|
+
...options,
|
|
142
|
+
simple: 'simple' in options ? options.simple : args.length === 0
|
|
143
|
+
})
|
|
144
|
+
return query
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function listen(name, fn, onlisten) {
|
|
149
|
+
const listener = { fn, onlisten }
|
|
150
|
+
|
|
151
|
+
const sql = listen.sql || (listen.sql = Postgres({
|
|
152
|
+
...options,
|
|
153
|
+
max: 1,
|
|
154
|
+
idle_timeout: null,
|
|
155
|
+
max_lifetime: null,
|
|
156
|
+
fetch_types: false,
|
|
157
|
+
onclose() {
|
|
158
|
+
Object.entries(listen.channels).forEach(([name, { listeners }]) => {
|
|
159
|
+
delete listen.channels[name]
|
|
160
|
+
Promise.all(listeners.map(l => listen(name, l.fn, l.onlisten).catch(() => { /* noop */ })))
|
|
161
|
+
})
|
|
162
|
+
},
|
|
163
|
+
onnotify(c, x) {
|
|
164
|
+
c in listen.channels && listen.channels[c].listeners.forEach(l => l.fn(x))
|
|
165
|
+
}
|
|
166
|
+
}))
|
|
167
|
+
|
|
168
|
+
const channels = listen.channels || (listen.channels = {})
|
|
169
|
+
, exists = name in channels
|
|
170
|
+
|
|
171
|
+
if (exists) {
|
|
172
|
+
channels[name].listeners.push(listener)
|
|
173
|
+
const result = await channels[name].result
|
|
174
|
+
listener.onlisten && listener.onlisten()
|
|
175
|
+
return { state: result.state, unlisten }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
channels[name] = { result: sql`listen ${
|
|
179
|
+
sql.unsafe('"' + name.replace(/"/g, '""') + '"')
|
|
180
|
+
}`, listeners: [listener] }
|
|
181
|
+
const result = await channels[name].result
|
|
182
|
+
listener.onlisten && listener.onlisten()
|
|
183
|
+
return { state: result.state, unlisten }
|
|
184
|
+
|
|
185
|
+
async function unlisten() {
|
|
186
|
+
if (name in channels === false)
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
channels[name].listeners = channels[name].listeners.filter(x => x !== listener)
|
|
190
|
+
if (channels[name].listeners.length)
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
delete channels[name]
|
|
194
|
+
return sql`unlisten ${
|
|
195
|
+
sql.unsafe('"' + name.replace(/"/g, '""') + '"')
|
|
196
|
+
}`
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function notify(channel, payload) {
|
|
201
|
+
return await sql`select pg_notify(${ channel }, ${ '' + payload })`
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function lease(auto) {
|
|
205
|
+
if (ending)
|
|
206
|
+
throw Errors.connection('CONNECTION_ENDED', options)
|
|
207
|
+
|
|
208
|
+
const l = { connection: null, closed: null, onclose: null, reserved: null, queue: Queue() }
|
|
209
|
+
open.length
|
|
210
|
+
? own(open.shift(), l)
|
|
211
|
+
: auto && !closed.length && busy.length
|
|
212
|
+
? own(busy.shift(), l)
|
|
213
|
+
: await new Promise((resolve, reject) => {
|
|
214
|
+
const query = { reserve: c => resolve(own(c, l)), reject }
|
|
215
|
+
closed.length
|
|
216
|
+
? connect(closed.shift(), query)
|
|
217
|
+
: queries.push(query)
|
|
218
|
+
})
|
|
219
|
+
return l
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function own(c, l) {
|
|
223
|
+
l.connection = c
|
|
224
|
+
c.lease = l
|
|
225
|
+
move(c, reserved)
|
|
226
|
+
c.reserved = l.reserved = () => l.queue.length
|
|
227
|
+
? c.execute(l.queue.shift())
|
|
228
|
+
: move(c, reserved)
|
|
229
|
+
c.reserved.release = true
|
|
230
|
+
return c
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function valid(l) {
|
|
234
|
+
return !l.closed && l.connection.lease === l && l.connection.reserved === l.reserved
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function send(l, q) {
|
|
238
|
+
const c = l.connection
|
|
239
|
+
valid(l)
|
|
240
|
+
? c.queue === full
|
|
241
|
+
? l.queue.push(q)
|
|
242
|
+
: c.execute(q) || move(c, full)
|
|
243
|
+
: q.reject(gone(l))
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function gone(l) {
|
|
247
|
+
return Errors.connection(l.closed && l.closed.code === 'CONNECTION_ENDED' ? 'CONNECTION_ENDED' : 'CONNECTION_CLOSED', options)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function finish(l) {
|
|
251
|
+
const c = l.connection
|
|
252
|
+
if (l.closed || c.lease !== l)
|
|
253
|
+
return false
|
|
254
|
+
|
|
255
|
+
const held = c.reserved === l.reserved
|
|
256
|
+
c.lease = null
|
|
257
|
+
l.closed = Errors.connection('CONNECTION_ENDED', options)
|
|
258
|
+
held && (c.reserved = null)
|
|
259
|
+
while (l.queue.length)
|
|
260
|
+
l.queue.shift().reject(gone(l))
|
|
261
|
+
return held
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function reserve() {
|
|
265
|
+
const l = await lease(false)
|
|
266
|
+
const sql = Sql(q => send(l, q))
|
|
267
|
+
sql.release = () => finish(l) && l.connection.release()
|
|
268
|
+
return sql
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function begin(options, fn) {
|
|
272
|
+
!fn && (fn = options, options = '')
|
|
273
|
+
const l = await lease(true)
|
|
274
|
+
let savepoints = 0
|
|
275
|
+
, prepare = null
|
|
276
|
+
, settled = false
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
const closing = new Promise((_, reject) => l.onclose = reject)
|
|
280
|
+
return await Promise.race([transaction(), closing])
|
|
281
|
+
} finally {
|
|
282
|
+
finish(l) && (settled ? l.connection.release() : l.connection.terminate())
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function transaction() {
|
|
286
|
+
await Sql(q => send(l, q)).unsafe('begin ' + options.replace(/[^a-z ]/ig, ''), []).execute().catch(e => {
|
|
287
|
+
e instanceof PostgresError && (settled = true)
|
|
288
|
+
throw e
|
|
289
|
+
})
|
|
290
|
+
return scope(fn)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function scope(fn, name) {
|
|
294
|
+
const sql = Sql(handler)
|
|
295
|
+
sql.savepoint = savepoint
|
|
296
|
+
sql.prepare = x => {
|
|
297
|
+
if (typeof x !== 'string' || !x || x.includes('\0'))
|
|
298
|
+
throw Errors.generic('INVALID_TRANSACTION_NAME', 'sql.prepare requires a non-empty string without null bytes')
|
|
299
|
+
|
|
300
|
+
prepare = 'E\'' + x.replace(/\\/g, '\\\\').replace(/'/g, '\'\'') + '\''
|
|
301
|
+
}
|
|
302
|
+
let uncaughtError
|
|
303
|
+
, result
|
|
304
|
+
|
|
305
|
+
name && await sql`savepoint ${ sql(name) }`
|
|
306
|
+
try {
|
|
307
|
+
result = await new Promise((resolve, reject) => {
|
|
308
|
+
const x = fn(sql)
|
|
309
|
+
Promise.resolve(Array.isArray(x) ? Promise.all(x) : x).then(resolve, reject)
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
if (uncaughtError)
|
|
313
|
+
throw uncaughtError
|
|
314
|
+
} catch (e) {
|
|
315
|
+
l.closed || await (name
|
|
316
|
+
? sql`rollback to ${ sql(name) }`
|
|
317
|
+
: sql`rollback`
|
|
318
|
+
)
|
|
319
|
+
name || (settled = true)
|
|
320
|
+
throw e instanceof PostgresError && e.code === '25P02' && uncaughtError || e
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (l.closed)
|
|
324
|
+
throw l.closed
|
|
325
|
+
|
|
326
|
+
if (!name) {
|
|
327
|
+
prepare
|
|
328
|
+
? await sql`prepare transaction ${ sql.unsafe(prepare) }`
|
|
329
|
+
: await sql`commit`
|
|
330
|
+
settled = true
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return result
|
|
334
|
+
|
|
335
|
+
function savepoint(name, fn) {
|
|
336
|
+
if (name && Array.isArray(name.raw))
|
|
337
|
+
return savepoint(sql => sql.apply(sql, arguments))
|
|
338
|
+
|
|
339
|
+
arguments.length === 1 && (fn = name, name = null)
|
|
340
|
+
return scope(fn, 's' + savepoints++ + (name ? '_' + name : ''))
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function handler(q) {
|
|
344
|
+
q.catch(e => uncaughtError || (uncaughtError = e))
|
|
345
|
+
send(l, q)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function move(c, queue) {
|
|
351
|
+
c.queue.remove(c)
|
|
352
|
+
queue.push(c)
|
|
353
|
+
c.queue = queue
|
|
354
|
+
queue === open
|
|
355
|
+
? c.idleTimer.start()
|
|
356
|
+
: c.idleTimer.cancel()
|
|
357
|
+
return c
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function json(x) {
|
|
361
|
+
return new Parameter(x, 3802)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function array(x, type) {
|
|
365
|
+
if (!Array.isArray(x))
|
|
366
|
+
return array(Array.from(arguments))
|
|
367
|
+
|
|
368
|
+
return new Parameter(x, type || (x.length ? inferType(x) || 25 : 0), options.shared.typeArrayMap)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function handler(query) {
|
|
372
|
+
if (ending)
|
|
373
|
+
return query.reject(Errors.connection('CONNECTION_ENDED', options, options))
|
|
374
|
+
|
|
375
|
+
if (open.length)
|
|
376
|
+
return go(open.shift(), query)
|
|
377
|
+
|
|
378
|
+
if (closed.length)
|
|
379
|
+
return connect(closed.shift(), query)
|
|
380
|
+
|
|
381
|
+
busy.length
|
|
382
|
+
? go(busy.shift(), query)
|
|
383
|
+
: queries.push(query)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function go(c, query) {
|
|
387
|
+
return c.execute(query)
|
|
388
|
+
? move(c, busy)
|
|
389
|
+
: move(c, full)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function cancel(query) {
|
|
393
|
+
return new Promise((resolve, reject) => {
|
|
394
|
+
query.state
|
|
395
|
+
? query.active
|
|
396
|
+
? Connection(options).cancel(query.state, resolve, reject)
|
|
397
|
+
: query.cancelled = { resolve, reject }
|
|
398
|
+
: (
|
|
399
|
+
queries.remove(query),
|
|
400
|
+
query.cancelled = true,
|
|
401
|
+
query.reject(Errors.generic('57014', 'canceling statement due to user request')),
|
|
402
|
+
resolve()
|
|
403
|
+
)
|
|
404
|
+
})
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async function end({ timeout = null } = {}) {
|
|
408
|
+
if (ending)
|
|
409
|
+
return ending
|
|
410
|
+
|
|
411
|
+
await 1
|
|
412
|
+
let timer
|
|
413
|
+
return ending = Promise.resolve().then(() => Promise.race([
|
|
414
|
+
new Promise(r => timeout !== null && (timer = setTimeout(() => destroy(r), timeout * 1000))),
|
|
415
|
+
Promise.all(connections.map(c => c.end()).concat(
|
|
416
|
+
listen.sql ? listen.sql.end({ timeout: 0 }) : [],
|
|
417
|
+
subscribe.sql ? subscribe.sql.end({ timeout: 0 }) : []
|
|
418
|
+
))
|
|
419
|
+
])).then(() => {
|
|
420
|
+
clearTimeout(timer)
|
|
421
|
+
while (queries.length)
|
|
422
|
+
queries.shift().reject(Errors.connection('CONNECTION_CLOSED', options))
|
|
423
|
+
})
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function close() {
|
|
427
|
+
await Promise.all(connections.map(c => c.end()))
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async function destroy(resolve) {
|
|
431
|
+
while (queries.length)
|
|
432
|
+
queries.shift().reject(Errors.connection('CONNECTION_DESTROYED', options))
|
|
433
|
+
await Promise.all(connections.map(c => c.terminate()))
|
|
434
|
+
resolve()
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function connect(c, query) {
|
|
438
|
+
move(c, connecting)
|
|
439
|
+
c.connect(query)
|
|
440
|
+
return c
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function onend(c) {
|
|
444
|
+
move(c, ended)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function onopen(c, initial) {
|
|
448
|
+
if (initial && initial.reserve)
|
|
449
|
+
return initial.reserve(move(c, reserved))
|
|
450
|
+
|
|
451
|
+
if (queries.length === 0)
|
|
452
|
+
return move(c, open)
|
|
453
|
+
|
|
454
|
+
let max = Math.ceil(queries.length / (connecting.length + 1))
|
|
455
|
+
, ready = true
|
|
456
|
+
|
|
457
|
+
while (ready && queries.length && max-- > 0) {
|
|
458
|
+
const query = queries.shift()
|
|
459
|
+
if (query.reserve)
|
|
460
|
+
return query.reserve(move(c, reserved))
|
|
461
|
+
|
|
462
|
+
ready = c.execute(query)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
ready
|
|
466
|
+
? move(c, busy)
|
|
467
|
+
: move(c, full)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function ondrain(c) {
|
|
471
|
+
if (!ending)
|
|
472
|
+
return false
|
|
473
|
+
|
|
474
|
+
let drained = false
|
|
475
|
+
let ready = true
|
|
476
|
+
while (ready && queries.length) {
|
|
477
|
+
const query = queries.shift()
|
|
478
|
+
query.reserve
|
|
479
|
+
? query.reject(Errors.connection('CONNECTION_ENDED', options))
|
|
480
|
+
: (drained = true, ready = c.execute(query))
|
|
481
|
+
}
|
|
482
|
+
return drained
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function abandon(l, e) {
|
|
486
|
+
l.connection.lease = null
|
|
487
|
+
l.closed = e || Errors.connection('CONNECTION_CLOSED', options)
|
|
488
|
+
while (l.queue.length)
|
|
489
|
+
l.queue.shift().reject(gone(l))
|
|
490
|
+
l.onclose && l.onclose(e)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function onclose(c, e) {
|
|
494
|
+
move(c, closed)
|
|
495
|
+
c.reserved = null
|
|
496
|
+
c.lease && abandon(c.lease, e)
|
|
497
|
+
options.onclose && options.onclose(c.id)
|
|
498
|
+
if (ending) {
|
|
499
|
+
while (queries.length)
|
|
500
|
+
queries.shift().reject(e)
|
|
501
|
+
} else if (queries.length && c.queue === closed) {
|
|
502
|
+
connect(c, queries.shift())
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function parseOptions(a, b) {
|
|
508
|
+
if (a && a.shared)
|
|
509
|
+
return a
|
|
510
|
+
|
|
511
|
+
const env = process.env // eslint-disable-line
|
|
512
|
+
, o = (!a || typeof a === 'string' ? b : a) || {}
|
|
513
|
+
, { url, multihost } = parseUrl(a)
|
|
514
|
+
, query = [...url.searchParams].reduce((a, [b, c]) => (a[b] = c, a), {})
|
|
515
|
+
, host = o.hostname || o.host || multihost || url.hostname || env.PGHOST || 'localhost'
|
|
516
|
+
, port = o.port || url.port || env.PGPORT || 5432
|
|
517
|
+
, user = o.user || o.username || url.username || env.PGUSERNAME || env.PGUSER || osUsername()
|
|
518
|
+
|
|
519
|
+
o.no_prepare && (o.prepare = false)
|
|
520
|
+
query.sslmode && (query.ssl = query.sslmode, delete query.sslmode)
|
|
521
|
+
'timeout' in o && (console.log('The timeout option is deprecated, use idle_timeout instead'), o.idle_timeout = o.timeout) // eslint-disable-line
|
|
522
|
+
query.sslrootcert === 'system' && (query.ssl = 'verify-full')
|
|
523
|
+
|
|
524
|
+
const ints = ['idle_timeout', 'connect_timeout', 'max_lifetime', 'max_pipeline', 'backoff', 'keep_alive']
|
|
525
|
+
const defaults = {
|
|
526
|
+
max : globalThis.Cloudflare ? 3 : 10,
|
|
527
|
+
ssl : false,
|
|
528
|
+
sslnegotiation : null,
|
|
529
|
+
idle_timeout : null,
|
|
530
|
+
connect_timeout : 30,
|
|
531
|
+
max_lifetime : max_lifetime,
|
|
532
|
+
max_pipeline : 100,
|
|
533
|
+
backoff : backoff,
|
|
534
|
+
keep_alive : 60,
|
|
535
|
+
prepare : true,
|
|
536
|
+
debug : false,
|
|
537
|
+
fetch_types : true,
|
|
538
|
+
publications : 'alltables',
|
|
539
|
+
target_session_attrs: null
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const options = {
|
|
543
|
+
host : Array.isArray(host) ? host : host.split(',').map(x => x.split(':')[0]),
|
|
544
|
+
port : Array.isArray(port) ? port : host.split(',').map(x => parseInt(x.split(':')[1] || port)),
|
|
545
|
+
path : o.path || host.indexOf('/') > -1 && host + '/.s.PGSQL.' + port,
|
|
546
|
+
database : o.database || o.db || (url.pathname || '').slice(1) || env.PGDATABASE || user,
|
|
547
|
+
user : user,
|
|
548
|
+
pass : o.pass || o.password || url.password || env.PGPASSWORD || '',
|
|
549
|
+
...Object.entries(defaults).reduce(
|
|
550
|
+
(acc, [k, d]) => {
|
|
551
|
+
const value = k in o ? o[k] : k in query
|
|
552
|
+
? (query[k] === 'disable' || query[k] === 'false' ? false : query[k])
|
|
553
|
+
: env['PG' + k.toUpperCase()] || d
|
|
554
|
+
acc[k] = typeof value === 'string' && ints.includes(k)
|
|
555
|
+
? +value
|
|
556
|
+
: value
|
|
557
|
+
return acc
|
|
558
|
+
},
|
|
559
|
+
{}
|
|
560
|
+
),
|
|
561
|
+
connection : {
|
|
562
|
+
application_name: env.PGAPPNAME || 'postgres.js',
|
|
563
|
+
...o.connection,
|
|
564
|
+
...Object.entries(query).reduce((acc, [k, v]) => (k in defaults || (acc[k] = v), acc), {})
|
|
565
|
+
},
|
|
566
|
+
types : o.types || {},
|
|
567
|
+
target_session_attrs: tsa(o, url, env),
|
|
568
|
+
onnotice : o.onnotice,
|
|
569
|
+
onnotify : o.onnotify,
|
|
570
|
+
onclose : o.onclose,
|
|
571
|
+
onparameter : o.onparameter,
|
|
572
|
+
socket : o.socket,
|
|
573
|
+
transform : parseTransform(o.transform || { undefined: undefined }),
|
|
574
|
+
parameters : {},
|
|
575
|
+
shared : { retries: 0, typeArrayMap: {} },
|
|
576
|
+
...mergeUserTypes(o.types)
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
!options.fetch_types && addDefaultArrayTypes(options)
|
|
580
|
+
return options
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function tsa(o, url, env) {
|
|
584
|
+
const x = o.target_session_attrs || url.searchParams.get('target_session_attrs') || env.PGTARGETSESSIONATTRS
|
|
585
|
+
if (!x || ['read-write', 'read-only', 'primary', 'standby', 'prefer-standby'].includes(x))
|
|
586
|
+
return x
|
|
587
|
+
|
|
588
|
+
throw new Error('target_session_attrs ' + x + ' is not supported')
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function backoff(retries) {
|
|
592
|
+
return (0.5 + Math.random() / 2) * Math.min(3 ** retries / 100, 20)
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function max_lifetime() {
|
|
596
|
+
return 60 * (30 + Math.random() * 30)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function parseTransform(x) {
|
|
600
|
+
return {
|
|
601
|
+
undefined: x.undefined,
|
|
602
|
+
column: {
|
|
603
|
+
from: typeof x.column === 'function' ? x.column : x.column && x.column.from,
|
|
604
|
+
to: x.column && x.column.to
|
|
605
|
+
},
|
|
606
|
+
value: {
|
|
607
|
+
from: typeof x.value === 'function' ? x.value : x.value && x.value.from,
|
|
608
|
+
to: x.value && x.value.to
|
|
609
|
+
},
|
|
610
|
+
row: {
|
|
611
|
+
from: typeof x.row === 'function' ? x.row : x.row && x.row.from,
|
|
612
|
+
to: x.row && x.row.to
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function parseUrl(url) {
|
|
618
|
+
if (!url || typeof url !== 'string')
|
|
619
|
+
return { url: { searchParams: new Map() } }
|
|
620
|
+
|
|
621
|
+
let host = url
|
|
622
|
+
host = host.slice(host.indexOf('://') + 3).split(/[?/]/)[0]
|
|
623
|
+
host = decodeURIComponent(host.slice(host.indexOf('@') + 1))
|
|
624
|
+
|
|
625
|
+
const urlObj = new URL(url.replace(host, host.split(',')[0]))
|
|
626
|
+
|
|
627
|
+
return {
|
|
628
|
+
url: {
|
|
629
|
+
username: decodeURIComponent(urlObj.username),
|
|
630
|
+
password: decodeURIComponent(urlObj.password),
|
|
631
|
+
host: urlObj.host,
|
|
632
|
+
hostname: urlObj.hostname,
|
|
633
|
+
port: urlObj.port,
|
|
634
|
+
pathname: urlObj.pathname,
|
|
635
|
+
searchParams: urlObj.searchParams
|
|
636
|
+
},
|
|
637
|
+
multihost: host.indexOf(',') > -1 && host
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function osUsername() {
|
|
642
|
+
try {
|
|
643
|
+
return os.userInfo().username // eslint-disable-line
|
|
644
|
+
} catch (_) {
|
|
645
|
+
return process.env.USERNAME || process.env.USER || process.env.LOGNAME // eslint-disable-line
|
|
646
|
+
}
|
|
647
|
+
}
|