@steadybrew/postgresjs 4.3.0 → 4.4.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.
- package/README.md +18 -2
- package/cf/src/connection.js +58 -19
- package/cf/src/errors.js +8 -0
- package/cf/src/index.js +47 -32
- package/cf/src/subscribe.js +122 -47
- package/cjs/src/connection.js +58 -19
- package/cjs/src/errors.js +8 -0
- package/cjs/src/index.js +47 -32
- package/cjs/src/subscribe.js +122 -47
- package/package.json +1 -1
- package/src/connection.js +58 -19
- package/src/errors.js +8 -0
- package/src/index.js +47 -32
- package/src/subscribe.js +122 -47
- package/types/index.d.ts +9 -9
package/README.md
CHANGED
|
@@ -592,6 +592,8 @@ for await (const chunk of readableStream) {
|
|
|
592
592
|
|
|
593
593
|
> **NOTE** This is a low-level API which does not provide any type safety. To make this work, you must match your [`copy query` parameters](https://www.postgresql.org/docs/14/sql-copy.html) correctly to your [Node.js stream read or write](https://nodejs.org/api/stream.html) code. Ensure [Node.js stream backpressure](https://nodejs.org/en/learn/modules/backpressuring-in-streams) is handled correctly to avoid memory exhaustion.
|
|
594
594
|
|
|
595
|
+
A `COPY` holds its connection until it finishes. Postgres.js cannot tell that a simple-protocol query is a `COPY` until the server answers, so it sends nothing behind a simple query whose text contains the word `copy` (`sql.unsafe()` or `sql.file()` without parameters, `.simple()`, `.readable()`, `.writable()`). Queries sent through `sql` meanwhile run on another connection or wait for one. With `max: 1`, or when every connection is running a `COPY`, a query awaited before the `COPY` is ended therefore waits forever. Statements on a `sql.reserve()` or `sql.begin()` handle reject with `COPY_IN_PROGRESS` while its `COPY` runs.
|
|
596
|
+
|
|
595
597
|
### Canceling Queries in Progress
|
|
596
598
|
|
|
597
599
|
Postgres.js supports, [canceling queries in progress](https://www.postgresql.org/docs/7.1/protocol-protocol.html#AEN39000). It works by opening a new connection with a protocol level startup message to cancel the current query running on a specific connection. That means there is no guarantee that the query will be canceled, and due to the possible race conditions it might even result in canceling another query. This is fine for long running queries, but in the case of high load and fast queries it might be better to simply ignore results instead of canceling.
|
|
@@ -869,7 +871,7 @@ const sql = postgres({
|
|
|
869
871
|
When you call `.listen`, a dedicated connection will be created to ensure that you receive notifications instantly. This connection will be used for any further calls to `.listen`. The connection will automatically reconnect according to a backoff reconnection pattern to not overload the database server.
|
|
870
872
|
|
|
871
873
|
### Listen `await sql.listen(channel, onnotify, [onlisten]) -> { state }`
|
|
872
|
-
`.listen` takes the channel name, a function to handle each notify, and an optional function to run every time listen is registered and ready (happens on initial connect and reconnects). It returns a promise which resolves once the `LISTEN` query to Postgres completes, or if there is already a listener active.
|
|
874
|
+
`.listen` takes the channel name, a function to handle each notify, and an optional function to run every time listen is registered and ready (happens on initial connect and reconnects). It returns a promise which resolves once the `LISTEN` query to Postgres completes, or if there is already a listener active. If either function throws, the error is rethrown as an uncaught exception, and the other listeners and the returned promise are not affected.
|
|
873
875
|
|
|
874
876
|
```js
|
|
875
877
|
await sql.listen('news', payload => {
|
|
@@ -922,10 +924,17 @@ const { unsubscribe } = await sql.subscribe(
|
|
|
922
924
|
},
|
|
923
925
|
() => {
|
|
924
926
|
// Callback on initial connect and potential reconnects
|
|
927
|
+
},
|
|
928
|
+
(error) => {
|
|
929
|
+
// Callback on connection errors and changes that could not be decoded
|
|
925
930
|
}
|
|
926
931
|
)
|
|
927
932
|
```
|
|
928
933
|
|
|
934
|
+
If any of these callbacks throws, the error is rethrown as an uncaught exception, and other subscribers and the replication stream are not affected.
|
|
935
|
+
|
|
936
|
+
`onerror` is called with errors of the replication connection, after which the subscription reconnects on a new replication slot and calls `onsubscribe` again (these are also logged with `console.error`, and changes committed while it was disconnected are not delivered; if the reconnect itself fails, it is retried with backoff, its error is logged with `console.error` and passed to `onerror`, and it never ends the process), and with errors thrown while a change is decoded by a [transform](#data-transformation) or a custom type parser. Such a change is skipped, and the replication stream and later changes carry on. A decode error goes only to the subscriptions that would have received the change, matched by operation and table; a subscription filtered by key gets every skipped change of its table, as the key is read from the decoded row, even if the table has no primary key. If a column name transform throws, each change of that table is skipped and reported until the server describes the table again, for example after `ALTER TABLE`. If one of the matching subscriptions has no `onerror`, the error is rethrown once as an uncaught exception, which ends the process under Node's default handling. `onerror` stays registered across reconnects until `unsubscribe()`.
|
|
937
|
+
|
|
929
938
|
### Subscribe pattern
|
|
930
939
|
|
|
931
940
|
You can subscribe to specific operations, tables, or even rows with primary keys.
|
|
@@ -1051,6 +1060,8 @@ const sql = postgres('postgres://username:password@host:port/database', {
|
|
|
1051
1060
|
})
|
|
1052
1061
|
```
|
|
1053
1062
|
|
|
1063
|
+
The `onnotice`, `onparameter`, `onnotify`, `onclose`, `onidle` and `debug` callbacks passed in the options are called synchronously from inside the driver. If one throws, the error is rethrown as an uncaught exception and the connection and its queries are not affected; under Node's default handling that ends the process.
|
|
1064
|
+
|
|
1054
1065
|
Note that `max_lifetime = 60 * (30 + Math.random() * 30)` by default. This resolves to an interval between 30 and 60 minutes to optimize for the benefits of prepared statements **and** working nicely with Linux's OOM killer.
|
|
1055
1066
|
|
|
1056
1067
|
### Dynamic passwords
|
|
@@ -1457,7 +1468,12 @@ If the only attempt times out, the error is `CONNECT_TIMEOUT`. Once earlier atte
|
|
|
1457
1468
|
##### COPY_IN_PROGRESS
|
|
1458
1469
|
> You cannot execute queries during copy
|
|
1459
1470
|
|
|
1460
|
-
This error is thrown
|
|
1471
|
+
This error is thrown for a statement on a `sql.reserve()` or `sql.begin()` handle while its copy operation (writable / readable) runs, including statements sent before the server started it. Queries through `sql` wait instead.
|
|
1472
|
+
|
|
1473
|
+
##### TRANSACTION_ROLLED_BACK
|
|
1474
|
+
> The server rolled the transaction back instead of committing it
|
|
1475
|
+
|
|
1476
|
+
This error is thrown by `sql.begin()` when the server answers the final `COMMIT` (or `PREPARE TRANSACTION`) with `ROLLBACK`, because the transaction was aborted by a statement whose error the callback did not surface, such as a failing query that was not awaited. Nothing was committed. When such a query's error was already recorded, it is the error's `cause`.
|
|
1461
1477
|
|
|
1462
1478
|
## TypeScript support
|
|
1463
1479
|
|
package/cf/src/connection.js
CHANGED
|
@@ -7,7 +7,7 @@ import Stream from 'node:stream'
|
|
|
7
7
|
import { performance } from '../polyfills.js'
|
|
8
8
|
|
|
9
9
|
import { stringify, handleValue, addArrayType } from './types.js'
|
|
10
|
-
import { Errors } from './errors.js'
|
|
10
|
+
import { Errors, callback } from './errors.js'
|
|
11
11
|
import Result from './result.js'
|
|
12
12
|
import Queue from './queue.js'
|
|
13
13
|
import clampedTimeout from './timeout.js'
|
|
@@ -15,7 +15,7 @@ import { SSLRequest, tlsConfig, cancelRequest } from './transport.js'
|
|
|
15
15
|
import { Query, CLOSE } from './query.js'
|
|
16
16
|
import b from './bytes.js'
|
|
17
17
|
|
|
18
|
-
export
|
|
18
|
+
export { Connection }
|
|
19
19
|
|
|
20
20
|
let uid = 1
|
|
21
21
|
|
|
@@ -28,6 +28,8 @@ const Sync = b().S().end()
|
|
|
28
28
|
const Phase = { Closed: 0, Backoff: 1, Connecting: 2, Initializing: 3, Ready: 4, Draining: 5, Closing: 6 }
|
|
29
29
|
const phaseNames = Object.keys(Phase)
|
|
30
30
|
|
|
31
|
+
const endsAbortedState = /^\s*(rollback|abort)\b/i
|
|
32
|
+
|
|
31
33
|
const retryRoutines = new Set([
|
|
32
34
|
'FetchPreparedStatement',
|
|
33
35
|
'RevalidateCachedQuery',
|
|
@@ -112,6 +114,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
112
114
|
, chunk = null
|
|
113
115
|
, nonce = null
|
|
114
116
|
, query = null
|
|
117
|
+
, barrier = null
|
|
115
118
|
, final = null
|
|
116
119
|
|
|
117
120
|
const check = globalThis[Symbol.for('postgres.js:check')]
|
|
@@ -226,6 +229,8 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
226
229
|
owner: null,
|
|
227
230
|
get active() { return query ? 1 : 0 },
|
|
228
231
|
get pipelined() { return sent.length },
|
|
232
|
+
get accepting() { return accepting() },
|
|
233
|
+
get copying() { return stream !== null },
|
|
229
234
|
get acquiring() { return acquisition && !acquisition.owner.cancelled ? 1 : 0 },
|
|
230
235
|
count: 0,
|
|
231
236
|
id
|
|
@@ -270,8 +275,12 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
270
275
|
return !query && sent.length === 0
|
|
271
276
|
}
|
|
272
277
|
|
|
278
|
+
function accepting() {
|
|
279
|
+
return !query || (!barrier && sent.length < max_pipeline)
|
|
280
|
+
}
|
|
281
|
+
|
|
273
282
|
function backoffMs() {
|
|
274
|
-
return (
|
|
283
|
+
return backoffSeconds(backoff, options.shared.retries) * 1000
|
|
275
284
|
}
|
|
276
285
|
|
|
277
286
|
function standbyPass() {
|
|
@@ -448,7 +457,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
448
457
|
queryError(pending, err)
|
|
449
458
|
pending.cancelled && pending.cancelled.resolve()
|
|
450
459
|
}
|
|
451
|
-
query = results = errorResponse = chunk = nextWriteTimer = null
|
|
460
|
+
query = results = errorResponse = chunk = nextWriteTimer = barrier = null
|
|
452
461
|
result = new Result()
|
|
453
462
|
rows = 0
|
|
454
463
|
nonce = serverSignature = null
|
|
@@ -512,8 +521,10 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
512
521
|
}
|
|
513
522
|
|
|
514
523
|
function submit(q) {
|
|
515
|
-
if (stream)
|
|
516
|
-
|
|
524
|
+
if (stream) {
|
|
525
|
+
queryError(q, Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy'))
|
|
526
|
+
return true
|
|
527
|
+
}
|
|
517
528
|
|
|
518
529
|
if (q.cancelled)
|
|
519
530
|
return true
|
|
@@ -527,17 +538,21 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
527
538
|
: (query = q, query.active = true)
|
|
528
539
|
|
|
529
540
|
build(q)
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
541
|
+
const written = write(toBuffer(q))
|
|
542
|
+
if (q.describeFirst || q.cursorFn || mayCopy(q))
|
|
543
|
+
barrier = q
|
|
544
|
+
return written && accepting()
|
|
534
545
|
} catch (error) {
|
|
535
546
|
sent.remove(q) || write(Sync)
|
|
536
547
|
queryError(q, error)
|
|
537
|
-
return
|
|
548
|
+
return accepting()
|
|
538
549
|
}
|
|
539
550
|
}
|
|
540
551
|
|
|
552
|
+
function mayCopy(q) {
|
|
553
|
+
return q.options.simple && /\bcopy\b/i.test(q.string)
|
|
554
|
+
}
|
|
555
|
+
|
|
541
556
|
function toBuffer(q) {
|
|
542
557
|
if (q.parameters.length >= 65534)
|
|
543
558
|
throw Errors.generic('MAX_PARAMETERS_EXCEEDED', 'Max number of parameters (65534) exceeded')
|
|
@@ -596,7 +611,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
596
611
|
? statements[q.signature]
|
|
597
612
|
: { string, types, name: q.prepare ? statementId + statementCount++ : '' }
|
|
598
613
|
|
|
599
|
-
typeof options.debug === 'function' && options.debug
|
|
614
|
+
typeof options.debug === 'function' && callback(options.debug, id, string, parameters, types)
|
|
600
615
|
}
|
|
601
616
|
|
|
602
617
|
function write(x, fn) {
|
|
@@ -767,18 +782,21 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
767
782
|
backendParameters[k] = v
|
|
768
783
|
if (options.parameters[k] !== v) {
|
|
769
784
|
options.parameters[k] = v
|
|
770
|
-
onparameter
|
|
785
|
+
callback(onparameter, k, v)
|
|
771
786
|
}
|
|
772
787
|
}
|
|
773
788
|
|
|
774
|
-
function ReadyForQuery() {
|
|
789
|
+
function ReadyForQuery(x) {
|
|
790
|
+
query === barrier && (barrier = null)
|
|
775
791
|
if (query) {
|
|
776
792
|
if (errorResponse) {
|
|
777
793
|
if (query.initialization)
|
|
778
794
|
return dispatch('initFailed', errorResponse)
|
|
795
|
+
query.prepared && retryRoutines.has(errorResponse.routine) && delete statements[query.signature]
|
|
779
796
|
query.retried
|
|
780
797
|
? errored(query.retried)
|
|
781
|
-
: query.prepared && retryRoutines.has(errorResponse.routine) && query.owner === connection.owner
|
|
798
|
+
: query.prepared && retryRoutines.has(errorResponse.routine) && query.owner === connection.owner && !barrier
|
|
799
|
+
&& (x[5] !== 69 || endsAbortedState.test(query.string))
|
|
782
800
|
? retry(query, errorResponse)
|
|
783
801
|
: errored(errorResponse)
|
|
784
802
|
} else {
|
|
@@ -1061,7 +1079,6 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1061
1079
|
}
|
|
1062
1080
|
|
|
1063
1081
|
function retry(q, error) {
|
|
1064
|
-
delete statements[q.signature]
|
|
1065
1082
|
q.retried = error
|
|
1066
1083
|
execute(q)
|
|
1067
1084
|
}
|
|
@@ -1072,7 +1089,8 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1072
1089
|
|
|
1073
1090
|
let index = 9
|
|
1074
1091
|
while (x[index++] !== 0);
|
|
1075
|
-
|
|
1092
|
+
callback(
|
|
1093
|
+
onnotify,
|
|
1076
1094
|
x.toString('utf8', 9, index - 1),
|
|
1077
1095
|
x.toString('utf8', index, x.length - 1)
|
|
1078
1096
|
)
|
|
@@ -1118,6 +1136,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1118
1136
|
}
|
|
1119
1137
|
})
|
|
1120
1138
|
query.resolve(stream)
|
|
1139
|
+
connection.owner && connection.owner.next()
|
|
1121
1140
|
}
|
|
1122
1141
|
|
|
1123
1142
|
function CopyOutResponse() {
|
|
@@ -1125,6 +1144,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1125
1144
|
read() { socket.resume() }
|
|
1126
1145
|
})
|
|
1127
1146
|
query.resolve(stream)
|
|
1147
|
+
connection.owner && connection.owner.next()
|
|
1128
1148
|
}
|
|
1129
1149
|
|
|
1130
1150
|
/* c8 ignore next 3 */
|
|
@@ -1147,6 +1167,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1147
1167
|
}
|
|
1148
1168
|
})
|
|
1149
1169
|
query.resolve(stream)
|
|
1170
|
+
connection.owner && connection.owner.next()
|
|
1150
1171
|
}
|
|
1151
1172
|
|
|
1152
1173
|
function CopyData(x) {
|
|
@@ -1159,10 +1180,12 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
|
|
|
1159
1180
|
}
|
|
1160
1181
|
|
|
1161
1182
|
function NoticeResponse(x) {
|
|
1183
|
+
if (onnotice === false)
|
|
1184
|
+
return
|
|
1185
|
+
|
|
1162
1186
|
onnotice
|
|
1163
|
-
? onnotice
|
|
1187
|
+
? callback(onnotice, parseError(x))
|
|
1164
1188
|
: console.log(parseError(x)) // eslint-disable-line
|
|
1165
|
-
|
|
1166
1189
|
}
|
|
1167
1190
|
|
|
1168
1191
|
/* c8 ignore next 3 */
|
|
@@ -1313,3 +1336,19 @@ function timer(fn, seconds) {
|
|
|
1313
1336
|
fn.apply(null, args)
|
|
1314
1337
|
}
|
|
1315
1338
|
}
|
|
1339
|
+
|
|
1340
|
+
export function defaultBackoff(retries) {
|
|
1341
|
+
return (0.5 + Math.random() / 2) * Math.min(3 ** retries / 100, 20)
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
export function backoffSeconds(backoff, retries) {
|
|
1345
|
+
if (typeof backoff !== 'function')
|
|
1346
|
+
return backoff
|
|
1347
|
+
|
|
1348
|
+
try {
|
|
1349
|
+
return backoff(retries)
|
|
1350
|
+
} catch (e) {
|
|
1351
|
+
callback(() => { throw e })
|
|
1352
|
+
return defaultBackoff(retries)
|
|
1353
|
+
}
|
|
1354
|
+
}
|
package/cf/src/errors.js
CHANGED
package/cf/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
Parameter,
|
|
10
10
|
Identifier,
|
|
11
11
|
Builder,
|
|
12
|
+
escapeIdentifier,
|
|
12
13
|
toPascal,
|
|
13
14
|
pascal,
|
|
14
15
|
toCamel,
|
|
@@ -20,12 +21,12 @@ import {
|
|
|
20
21
|
fromKebab
|
|
21
22
|
} from './types.js'
|
|
22
23
|
|
|
23
|
-
import Connection from './connection.js'
|
|
24
|
+
import { Connection, defaultBackoff } from './connection.js'
|
|
24
25
|
import { cancelRequest } from './transport.js'
|
|
25
26
|
import clampedTimeout from './timeout.js'
|
|
26
27
|
import { Query, CLOSE } from './query.js'
|
|
27
28
|
import Queue from './queue.js'
|
|
28
|
-
import { Errors, PostgresError } from './errors.js'
|
|
29
|
+
import { Errors, PostgresError, callback } from './errors.js'
|
|
29
30
|
import Subscribe from './subscribe.js'
|
|
30
31
|
import largeObject from './large.js'
|
|
31
32
|
|
|
@@ -219,7 +220,7 @@ function Postgres(a, b) {
|
|
|
219
220
|
})
|
|
220
221
|
},
|
|
221
222
|
onnotify(c, x) {
|
|
222
|
-
c in listen.channels && listen.channels[c].listeners.forEach(l => l.fn
|
|
223
|
+
c in listen.channels && listen.channels[c].listeners.forEach(l => callback(l.fn, x))
|
|
223
224
|
}
|
|
224
225
|
}))
|
|
225
226
|
|
|
@@ -229,7 +230,7 @@ function Postgres(a, b) {
|
|
|
229
230
|
if (exists) {
|
|
230
231
|
channels[name].listeners.push(listener)
|
|
231
232
|
const result = await channels[name].result
|
|
232
|
-
listener.onlisten
|
|
233
|
+
callback(listener.onlisten)
|
|
233
234
|
return { state: result.state, unlisten }
|
|
234
235
|
}
|
|
235
236
|
|
|
@@ -237,7 +238,7 @@ function Postgres(a, b) {
|
|
|
237
238
|
sql.unsafe('"' + name.replace(/"/g, '""') + '"')
|
|
238
239
|
}`, listeners: [listener] }
|
|
239
240
|
const result = await channels[name].result
|
|
240
|
-
listener.onlisten
|
|
241
|
+
callback(listener.onlisten)
|
|
241
242
|
return { state: result.state, unlisten }
|
|
242
243
|
|
|
243
244
|
async function unlisten() {
|
|
@@ -295,9 +296,9 @@ function Postgres(a, b) {
|
|
|
295
296
|
function send(l, q, expected = Lease.Active) {
|
|
296
297
|
const c = l.connection
|
|
297
298
|
l.state === expected
|
|
298
|
-
? c.queue
|
|
299
|
-
? (q
|
|
300
|
-
:
|
|
299
|
+
? c.copying || (c.queue !== full && c.accepting)
|
|
300
|
+
? c.execute(q) || move(c, full)
|
|
301
|
+
: (move(c, full), q.queue = l.queue, l.queue.push(q))
|
|
301
302
|
: q.reject(leaseError(l.state))
|
|
302
303
|
}
|
|
303
304
|
|
|
@@ -353,14 +354,16 @@ function Postgres(a, b) {
|
|
|
353
354
|
}
|
|
354
355
|
|
|
355
356
|
async function finish(statement) {
|
|
357
|
+
let result
|
|
356
358
|
l.state = Lease.Settling
|
|
357
359
|
try {
|
|
358
|
-
await statement(Sql(q => send(l, q, Lease.Settling)))
|
|
360
|
+
result = await statement(Sql(q => send(l, q, Lease.Settling)))
|
|
359
361
|
} catch (e) {
|
|
360
362
|
settle(l, Lease.Discarded)
|
|
361
363
|
throw e
|
|
362
364
|
}
|
|
363
365
|
settle(l, Lease.Released)
|
|
366
|
+
return result
|
|
364
367
|
}
|
|
365
368
|
|
|
366
369
|
async function scope(fn, name) {
|
|
@@ -375,7 +378,7 @@ function Postgres(a, b) {
|
|
|
375
378
|
let uncaughtError
|
|
376
379
|
, result
|
|
377
380
|
|
|
378
|
-
name && await sql
|
|
381
|
+
name && await sql.unsafe('savepoint ' + escapeIdentifier(name))
|
|
379
382
|
try {
|
|
380
383
|
result = await new Promise((resolve, reject) => {
|
|
381
384
|
const x = fn(sql)
|
|
@@ -386,8 +389,8 @@ function Postgres(a, b) {
|
|
|
386
389
|
throw uncaughtError
|
|
387
390
|
} catch (e) {
|
|
388
391
|
l.state === Lease.Active && await (name
|
|
389
|
-
? sql
|
|
390
|
-
: finish(final => final
|
|
392
|
+
? sql.unsafe('rollback to ' + escapeIdentifier(name))
|
|
393
|
+
: finish(final => final.unsafe('rollback'))
|
|
391
394
|
)
|
|
392
395
|
throw e instanceof PostgresError && e.code === '25P02' && uncaughtError || e
|
|
393
396
|
}
|
|
@@ -395,10 +398,18 @@ function Postgres(a, b) {
|
|
|
395
398
|
if (l.state !== Lease.Active)
|
|
396
399
|
throw l.error || leaseError(l.state)
|
|
397
400
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
401
|
+
if (!name) {
|
|
402
|
+
const done = await finish(final => prepare
|
|
403
|
+
? final.unsafe('prepare transaction ' + prepare)
|
|
404
|
+
: final.unsafe('commit')
|
|
405
|
+
)
|
|
406
|
+
if (done.command === 'ROLLBACK') {
|
|
407
|
+
throw Object.assign(
|
|
408
|
+
Errors.generic('TRANSACTION_ROLLED_BACK', 'The server rolled the transaction back instead of committing it'),
|
|
409
|
+
uncaughtError && { cause: uncaughtError }
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
402
413
|
|
|
403
414
|
return result
|
|
404
415
|
|
|
@@ -427,14 +438,6 @@ function Postgres(a, b) {
|
|
|
427
438
|
return c
|
|
428
439
|
}
|
|
429
440
|
|
|
430
|
-
function callback(fn, ...args) {
|
|
431
|
-
try {
|
|
432
|
-
fn && fn(...args)
|
|
433
|
-
} catch (e) {
|
|
434
|
-
queueMicrotask(() => { throw e })
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
441
|
function json(x) {
|
|
439
442
|
return new Parameter(x, 3802)
|
|
440
443
|
}
|
|
@@ -457,11 +460,24 @@ function Postgres(a, b) {
|
|
|
457
460
|
if (closed.length)
|
|
458
461
|
return connect(closed.shift(), query)
|
|
459
462
|
|
|
460
|
-
|
|
461
|
-
|
|
463
|
+
const c = takeBusy()
|
|
464
|
+
c
|
|
465
|
+
? go(c, query)
|
|
462
466
|
: queries.push(query)
|
|
463
467
|
}
|
|
464
468
|
|
|
469
|
+
function takeBusy() {
|
|
470
|
+
while (busy.length) {
|
|
471
|
+
const c = busy.shift()
|
|
472
|
+
if (c.accepting)
|
|
473
|
+
return c
|
|
474
|
+
|
|
475
|
+
move(c, full)
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return null
|
|
479
|
+
}
|
|
480
|
+
|
|
465
481
|
function takeOpen() {
|
|
466
482
|
while (open.length) {
|
|
467
483
|
const c = open.shift()
|
|
@@ -546,6 +562,9 @@ function Postgres(a, b) {
|
|
|
546
562
|
if (initial && initial.reserve)
|
|
547
563
|
return initial.reserve(c)
|
|
548
564
|
|
|
565
|
+
if (!initial && !c.accepting)
|
|
566
|
+
return move(c, full)
|
|
567
|
+
|
|
549
568
|
if (!initial && !queries.length)
|
|
550
569
|
return move(c, open)
|
|
551
570
|
|
|
@@ -584,7 +603,7 @@ function Postgres(a, b) {
|
|
|
584
603
|
function onclose(c, e) {
|
|
585
604
|
move(c, closed)
|
|
586
605
|
c.owner && settle(c.owner, Lease.Closed, e)
|
|
587
|
-
options.onclose
|
|
606
|
+
callback(options.onclose, c.id)
|
|
588
607
|
if (ending)
|
|
589
608
|
return closedWhileEnding(c, e)
|
|
590
609
|
|
|
@@ -639,7 +658,7 @@ function parseOptions(a, b) {
|
|
|
639
658
|
connect_timeout : 30,
|
|
640
659
|
max_lifetime : max_lifetime,
|
|
641
660
|
max_pipeline : 100,
|
|
642
|
-
backoff :
|
|
661
|
+
backoff : defaultBackoff,
|
|
643
662
|
keep_alive : 60,
|
|
644
663
|
prepare : true,
|
|
645
664
|
debug : false,
|
|
@@ -698,10 +717,6 @@ function tsa(o, url, env) {
|
|
|
698
717
|
throw new Error('target_session_attrs ' + x + ' is not supported')
|
|
699
718
|
}
|
|
700
719
|
|
|
701
|
-
function backoff(retries) {
|
|
702
|
-
return (0.5 + Math.random() / 2) * Math.min(3 ** retries / 100, 20)
|
|
703
|
-
}
|
|
704
|
-
|
|
705
720
|
function max_lifetime() {
|
|
706
721
|
return 60 * (30 + Math.random() * 30)
|
|
707
722
|
}
|