@steadybrew/postgresjs 4.2.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 +70 -2
- package/cf/src/connection.js +63 -21
- package/cf/src/errors.js +8 -0
- package/cf/src/index.js +100 -33
- package/cf/src/subscribe.js +122 -47
- package/cjs/src/connection.js +63 -21
- package/cjs/src/errors.js +8 -0
- package/cjs/src/index.js +100 -33
- package/cjs/src/subscribe.js +122 -47
- package/package.json +1 -1
- package/src/connection.js +63 -21
- package/src/errors.js +8 -0
- package/src/index.js +100 -33
- package/src/subscribe.js +122 -47
- package/types/index.d.ts +41 -9
package/cjs/src/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
Parameter,
|
|
9
9
|
Identifier,
|
|
10
10
|
Builder,
|
|
11
|
+
escapeIdentifier,
|
|
11
12
|
toPascal,
|
|
12
13
|
pascal,
|
|
13
14
|
toCamel,
|
|
@@ -19,12 +20,12 @@ const {
|
|
|
19
20
|
fromKebab
|
|
20
21
|
} = require('./types.js')
|
|
21
22
|
|
|
22
|
-
const Connection = require('./connection.js')
|
|
23
|
+
const { Connection, defaultBackoff } = require('./connection.js')
|
|
23
24
|
const { cancelRequest } = require('./transport.js')
|
|
24
25
|
const clampedTimeout = require('./timeout.js')
|
|
25
26
|
const { Query, CLOSE } = require('./query.js')
|
|
26
27
|
const Queue = require('./queue.js')
|
|
27
|
-
const { Errors, PostgresError } = require('./errors.js')
|
|
28
|
+
const { Errors, PostgresError, callback } = require('./errors.js')
|
|
28
29
|
const Subscribe = require('./subscribe.js')
|
|
29
30
|
const largeObject = require('./large.js')
|
|
30
31
|
|
|
@@ -81,6 +82,7 @@ function Postgres(a, b) {
|
|
|
81
82
|
PostgresError,
|
|
82
83
|
options,
|
|
83
84
|
reserve,
|
|
85
|
+
stats,
|
|
84
86
|
listen,
|
|
85
87
|
begin,
|
|
86
88
|
close,
|
|
@@ -150,6 +152,56 @@ function Postgres(a, b) {
|
|
|
150
152
|
}
|
|
151
153
|
}
|
|
152
154
|
|
|
155
|
+
function stats() {
|
|
156
|
+
let idleCount = 0
|
|
157
|
+
, busyCount = 0
|
|
158
|
+
, reservedCount = 0
|
|
159
|
+
, connectingCount = 0
|
|
160
|
+
, closingCount = 0
|
|
161
|
+
, executingCount = 0
|
|
162
|
+
, waitingCount = queries.length
|
|
163
|
+
|
|
164
|
+
for (const c of connections) {
|
|
165
|
+
executingCount += c.active
|
|
166
|
+
waitingCount += c.pipelined + c.acquiring + (c.owner ? c.owner.queue.length : 0)
|
|
167
|
+
c.queue === open
|
|
168
|
+
? idleCount++
|
|
169
|
+
: c.queue === connecting
|
|
170
|
+
? connectingCount++
|
|
171
|
+
: c.queue === ended
|
|
172
|
+
? closingCount++
|
|
173
|
+
: c.queue === reserved
|
|
174
|
+
? reservedCount++
|
|
175
|
+
: (c.queue === busy || c.queue === full) && (c.owner ? reservedCount++ : busyCount++)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const totalCount = idleCount + busyCount + reservedCount + connectingCount
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
max: options.max,
|
|
182
|
+
totalCount,
|
|
183
|
+
idleCount,
|
|
184
|
+
waitingCount,
|
|
185
|
+
activeCount: busyCount + reservedCount,
|
|
186
|
+
availableCount: idleCount + options.max - totalCount - closingCount,
|
|
187
|
+
busyCount,
|
|
188
|
+
reservedCount,
|
|
189
|
+
connectingCount,
|
|
190
|
+
closingCount,
|
|
191
|
+
executingCount,
|
|
192
|
+
listenCount: pooled(listen.sql),
|
|
193
|
+
subscribeCount: pooled(subscribe.sql)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function pooled(sql) {
|
|
198
|
+
if (!sql)
|
|
199
|
+
return 0
|
|
200
|
+
|
|
201
|
+
const s = sql.stats()
|
|
202
|
+
return s.totalCount + s.closingCount
|
|
203
|
+
}
|
|
204
|
+
|
|
153
205
|
async function listen(name, fn, onlisten) {
|
|
154
206
|
const listener = { fn, onlisten }
|
|
155
207
|
|
|
@@ -167,7 +219,7 @@ function Postgres(a, b) {
|
|
|
167
219
|
})
|
|
168
220
|
},
|
|
169
221
|
onnotify(c, x) {
|
|
170
|
-
c in listen.channels && listen.channels[c].listeners.forEach(l => l.fn
|
|
222
|
+
c in listen.channels && listen.channels[c].listeners.forEach(l => callback(l.fn, x))
|
|
171
223
|
}
|
|
172
224
|
}))
|
|
173
225
|
|
|
@@ -177,7 +229,7 @@ function Postgres(a, b) {
|
|
|
177
229
|
if (exists) {
|
|
178
230
|
channels[name].listeners.push(listener)
|
|
179
231
|
const result = await channels[name].result
|
|
180
|
-
listener.onlisten
|
|
232
|
+
callback(listener.onlisten)
|
|
181
233
|
return { state: result.state, unlisten }
|
|
182
234
|
}
|
|
183
235
|
|
|
@@ -185,7 +237,7 @@ function Postgres(a, b) {
|
|
|
185
237
|
sql.unsafe('"' + name.replace(/"/g, '""') + '"')
|
|
186
238
|
}`, listeners: [listener] }
|
|
187
239
|
const result = await channels[name].result
|
|
188
|
-
listener.onlisten
|
|
240
|
+
callback(listener.onlisten)
|
|
189
241
|
return { state: result.state, unlisten }
|
|
190
242
|
|
|
191
243
|
async function unlisten() {
|
|
@@ -243,9 +295,9 @@ function Postgres(a, b) {
|
|
|
243
295
|
function send(l, q, expected = Lease.Active) {
|
|
244
296
|
const c = l.connection
|
|
245
297
|
l.state === expected
|
|
246
|
-
? c.queue
|
|
247
|
-
?
|
|
248
|
-
:
|
|
298
|
+
? c.copying || (c.queue !== full && c.accepting)
|
|
299
|
+
? c.execute(q) || move(c, full)
|
|
300
|
+
: (move(c, full), q.queue = l.queue, l.queue.push(q))
|
|
249
301
|
: q.reject(leaseError(l.state))
|
|
250
302
|
}
|
|
251
303
|
|
|
@@ -275,6 +327,7 @@ function Postgres(a, b) {
|
|
|
275
327
|
const l = await lease(false)
|
|
276
328
|
const sql = Sql(q => send(l, q))
|
|
277
329
|
sql.release = () => settle(l, Lease.Released)
|
|
330
|
+
sql.stats = stats
|
|
278
331
|
return sql
|
|
279
332
|
}
|
|
280
333
|
|
|
@@ -300,14 +353,16 @@ function Postgres(a, b) {
|
|
|
300
353
|
}
|
|
301
354
|
|
|
302
355
|
async function finish(statement) {
|
|
356
|
+
let result
|
|
303
357
|
l.state = Lease.Settling
|
|
304
358
|
try {
|
|
305
|
-
await statement(Sql(q => send(l, q, Lease.Settling)))
|
|
359
|
+
result = await statement(Sql(q => send(l, q, Lease.Settling)))
|
|
306
360
|
} catch (e) {
|
|
307
361
|
settle(l, Lease.Discarded)
|
|
308
362
|
throw e
|
|
309
363
|
}
|
|
310
364
|
settle(l, Lease.Released)
|
|
365
|
+
return result
|
|
311
366
|
}
|
|
312
367
|
|
|
313
368
|
async function scope(fn, name) {
|
|
@@ -322,7 +377,7 @@ function Postgres(a, b) {
|
|
|
322
377
|
let uncaughtError
|
|
323
378
|
, result
|
|
324
379
|
|
|
325
|
-
name && await sql
|
|
380
|
+
name && await sql.unsafe('savepoint ' + escapeIdentifier(name))
|
|
326
381
|
try {
|
|
327
382
|
result = await new Promise((resolve, reject) => {
|
|
328
383
|
const x = fn(sql)
|
|
@@ -333,8 +388,8 @@ function Postgres(a, b) {
|
|
|
333
388
|
throw uncaughtError
|
|
334
389
|
} catch (e) {
|
|
335
390
|
l.state === Lease.Active && await (name
|
|
336
|
-
? sql
|
|
337
|
-
: finish(final => final
|
|
391
|
+
? sql.unsafe('rollback to ' + escapeIdentifier(name))
|
|
392
|
+
: finish(final => final.unsafe('rollback'))
|
|
338
393
|
)
|
|
339
394
|
throw e instanceof PostgresError && e.code === '25P02' && uncaughtError || e
|
|
340
395
|
}
|
|
@@ -342,10 +397,18 @@ function Postgres(a, b) {
|
|
|
342
397
|
if (l.state !== Lease.Active)
|
|
343
398
|
throw l.error || leaseError(l.state)
|
|
344
399
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
400
|
+
if (!name) {
|
|
401
|
+
const done = await finish(final => prepare
|
|
402
|
+
? final.unsafe('prepare transaction ' + prepare)
|
|
403
|
+
: final.unsafe('commit')
|
|
404
|
+
)
|
|
405
|
+
if (done.command === 'ROLLBACK') {
|
|
406
|
+
throw Object.assign(
|
|
407
|
+
Errors.generic('TRANSACTION_ROLLED_BACK', 'The server rolled the transaction back instead of committing it'),
|
|
408
|
+
uncaughtError && { cause: uncaughtError }
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
}
|
|
349
412
|
|
|
350
413
|
return result
|
|
351
414
|
|
|
@@ -374,14 +437,6 @@ function Postgres(a, b) {
|
|
|
374
437
|
return c
|
|
375
438
|
}
|
|
376
439
|
|
|
377
|
-
function callback(fn, ...args) {
|
|
378
|
-
try {
|
|
379
|
-
fn && fn(...args)
|
|
380
|
-
} catch (e) {
|
|
381
|
-
queueMicrotask(() => { throw e })
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
440
|
function json(x) {
|
|
386
441
|
return new Parameter(x, 3802)
|
|
387
442
|
}
|
|
@@ -404,11 +459,24 @@ function Postgres(a, b) {
|
|
|
404
459
|
if (closed.length)
|
|
405
460
|
return connect(closed.shift(), query)
|
|
406
461
|
|
|
407
|
-
|
|
408
|
-
|
|
462
|
+
const c = takeBusy()
|
|
463
|
+
c
|
|
464
|
+
? go(c, query)
|
|
409
465
|
: queries.push(query)
|
|
410
466
|
}
|
|
411
467
|
|
|
468
|
+
function takeBusy() {
|
|
469
|
+
while (busy.length) {
|
|
470
|
+
const c = busy.shift()
|
|
471
|
+
if (c.accepting)
|
|
472
|
+
return c
|
|
473
|
+
|
|
474
|
+
move(c, full)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return null
|
|
478
|
+
}
|
|
479
|
+
|
|
412
480
|
function takeOpen() {
|
|
413
481
|
while (open.length) {
|
|
414
482
|
const c = open.shift()
|
|
@@ -435,7 +503,7 @@ function Postgres(a, b) {
|
|
|
435
503
|
if (query.state) {
|
|
436
504
|
query.cancelled = { resolve, reject }
|
|
437
505
|
} else {
|
|
438
|
-
queries.remove(query)
|
|
506
|
+
(query.queue || queries).remove(query)
|
|
439
507
|
query.cancelled = true
|
|
440
508
|
query.reject(Errors.generic('57014', 'canceling statement due to user request'))
|
|
441
509
|
resolve()
|
|
@@ -493,6 +561,9 @@ function Postgres(a, b) {
|
|
|
493
561
|
if (initial && initial.reserve)
|
|
494
562
|
return initial.reserve(c)
|
|
495
563
|
|
|
564
|
+
if (!initial && !c.accepting)
|
|
565
|
+
return move(c, full)
|
|
566
|
+
|
|
496
567
|
if (!initial && !queries.length)
|
|
497
568
|
return move(c, open)
|
|
498
569
|
|
|
@@ -531,7 +602,7 @@ function Postgres(a, b) {
|
|
|
531
602
|
function onclose(c, e) {
|
|
532
603
|
move(c, closed)
|
|
533
604
|
c.owner && settle(c.owner, Lease.Closed, e)
|
|
534
|
-
options.onclose
|
|
605
|
+
callback(options.onclose, c.id)
|
|
535
606
|
if (ending)
|
|
536
607
|
return closedWhileEnding(c, e)
|
|
537
608
|
|
|
@@ -586,7 +657,7 @@ function parseOptions(a, b) {
|
|
|
586
657
|
connect_timeout : 30,
|
|
587
658
|
max_lifetime : max_lifetime,
|
|
588
659
|
max_pipeline : 100,
|
|
589
|
-
backoff :
|
|
660
|
+
backoff : defaultBackoff,
|
|
590
661
|
keep_alive : 60,
|
|
591
662
|
prepare : true,
|
|
592
663
|
debug : false,
|
|
@@ -645,10 +716,6 @@ function tsa(o, url, env) {
|
|
|
645
716
|
throw new Error('target_session_attrs ' + x + ' is not supported')
|
|
646
717
|
}
|
|
647
718
|
|
|
648
|
-
function backoff(retries) {
|
|
649
|
-
return (0.5 + Math.random() / 2) * Math.min(3 ** retries / 100, 20)
|
|
650
|
-
}
|
|
651
|
-
|
|
652
719
|
function max_lifetime() {
|
|
653
720
|
return 60 * (30 + Math.random() * 30)
|
|
654
721
|
}
|
package/cjs/src/subscribe.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
const { callback } = require('./errors.js')
|
|
2
|
+
const { defaultBackoff, backoffSeconds } = require('./connection.js')
|
|
3
|
+
|
|
1
4
|
const noop = () => { /* noop */ }
|
|
5
|
+
, commands = { 68: 'delete', 73: 'insert', 85: 'update' }
|
|
2
6
|
|
|
3
7
|
module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
4
8
|
const subscribers = new Map()
|
|
@@ -7,6 +11,9 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
7
11
|
|
|
8
12
|
let connection
|
|
9
13
|
, stream
|
|
14
|
+
, timer
|
|
15
|
+
, inflight = false
|
|
16
|
+
, retries = 0
|
|
10
17
|
, ended = false
|
|
11
18
|
|
|
12
19
|
const sql = subscribe.sql = postgres({
|
|
@@ -21,13 +28,12 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
21
28
|
...options.connection,
|
|
22
29
|
replication: 'database'
|
|
23
30
|
},
|
|
24
|
-
onclose:
|
|
31
|
+
onclose: function() {
|
|
25
32
|
if (ended)
|
|
26
33
|
return
|
|
27
34
|
stream = null
|
|
28
35
|
state.pid = state.secret = undefined
|
|
29
|
-
|
|
30
|
-
subscribers.forEach(event => event.forEach(({ onsubscribe }) => onsubscribe()))
|
|
36
|
+
inflight || timer || !connection || connect().catch(noop)
|
|
31
37
|
},
|
|
32
38
|
no_subscribe: true
|
|
33
39
|
})
|
|
@@ -37,6 +43,9 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
37
43
|
|
|
38
44
|
sql.end = async() => {
|
|
39
45
|
ended = true
|
|
46
|
+
clearTimeout(timer)
|
|
47
|
+
timer = null
|
|
48
|
+
connection && await connection.catch(noop)
|
|
40
49
|
stream && (await new Promise(r => (stream.once('close', r), stream.end())))
|
|
41
50
|
return end()
|
|
42
51
|
}
|
|
@@ -48,34 +57,74 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
48
57
|
|
|
49
58
|
return subscribe
|
|
50
59
|
|
|
51
|
-
async function subscribe(event, fn, onsubscribe = noop, onerror
|
|
60
|
+
async function subscribe(event, fn, onsubscribe = noop, onerror) {
|
|
52
61
|
event = parseEvent(event)
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
connection = init(sql, slot, options.publications)
|
|
63
|
+
const subscriber = { fn, onsubscribe, onerror }
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
return (connection || connect()).then(() => {
|
|
66
|
+
const fns = subscribers.has(event)
|
|
67
|
+
? subscribers.get(event).add(subscriber)
|
|
68
|
+
: subscribers.set(event, new Set([subscriber])).get(event)
|
|
61
69
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
const unsubscribe = () => {
|
|
71
|
+
fns.delete(subscriber)
|
|
72
|
+
fns.size === 0 && subscribers.delete(event)
|
|
73
|
+
subscribers.size === 0 && (clearTimeout(timer), timer = null)
|
|
74
|
+
}
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
connected(x)
|
|
69
|
-
onsubscribe()
|
|
70
|
-
stream && stream.on('error', onerror)
|
|
76
|
+
callback(onsubscribe)
|
|
71
77
|
return { unsubscribe, state, sql }
|
|
72
78
|
})
|
|
73
79
|
}
|
|
74
80
|
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
function connect() {
|
|
82
|
+
clearTimeout(timer)
|
|
83
|
+
timer = null
|
|
84
|
+
inflight = true
|
|
85
|
+
const resume = subscribers.size > 0
|
|
86
|
+
const current = connection = init(sql, slot, options.publications).then(x => {
|
|
87
|
+
stream = x.stream
|
|
88
|
+
state.pid = x.state.pid
|
|
89
|
+
state.secret = x.state.secret
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
current.then(
|
|
93
|
+
() => {
|
|
94
|
+
inflight = false
|
|
95
|
+
retries = 0
|
|
96
|
+
resume && !ended && subscribers.forEach(event => event.forEach(({ onsubscribe }) => callback(onsubscribe)))
|
|
97
|
+
},
|
|
98
|
+
error => {
|
|
99
|
+
inflight = false
|
|
100
|
+
connection === current && (connection = null)
|
|
101
|
+
if (ended || subscribers.size === 0)
|
|
102
|
+
return
|
|
103
|
+
reportConnection(error)
|
|
104
|
+
timer = setTimeout(() => connect().catch(noop), retryDelay(retries++))
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return current
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function retryDelay(n) {
|
|
112
|
+
let seconds = backoffSeconds(sql.options.backoff, n)
|
|
113
|
+
seconds = Number.isFinite(seconds) ? seconds : defaultBackoff(n)
|
|
114
|
+
return Math.min(Math.max(seconds * 1000, 100), 2 ** 31 - 1)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function reportConnection(error) {
|
|
118
|
+
console.error('Unexpected error during logical streaming - reconnecting', error) // eslint-disable-line
|
|
119
|
+
subscribers.forEach(fns => fns.forEach(({ onerror }) => callback(onerror, error)))
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function report(error, match) {
|
|
123
|
+
let uncaught = false
|
|
124
|
+
subscribers.forEach((fns, event) => match(event) && fns.forEach(({ onerror }) =>
|
|
125
|
+
onerror ? callback(onerror, error) : uncaught = true
|
|
126
|
+
))
|
|
127
|
+
uncaught && callback(() => { throw error })
|
|
79
128
|
}
|
|
80
129
|
|
|
81
130
|
async function init(sql, slot, publications) {
|
|
@@ -99,18 +148,18 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
99
148
|
}
|
|
100
149
|
|
|
101
150
|
stream.on('data', data)
|
|
102
|
-
stream.on('error',
|
|
151
|
+
stream.on('error', reportConnection)
|
|
103
152
|
stream.on('close', sql.close)
|
|
104
153
|
|
|
105
154
|
return { stream, state: xs.state }
|
|
106
155
|
|
|
107
|
-
function error(e) {
|
|
108
|
-
console.error('Unexpected error during logical streaming - reconnecting', e) // eslint-disable-line
|
|
109
|
-
}
|
|
110
|
-
|
|
111
156
|
function data(x) {
|
|
112
157
|
if (x[0] === 0x77) {
|
|
113
|
-
|
|
158
|
+
try {
|
|
159
|
+
parse(x.subarray(25), state, sql.options.parsers, handle, options.transform)
|
|
160
|
+
} catch (error) {
|
|
161
|
+
skip(x.subarray(25), error)
|
|
162
|
+
}
|
|
114
163
|
} else if (x[0] === 0x6b && x[17]) {
|
|
115
164
|
state.lsn = x.subarray(1, 9)
|
|
116
165
|
pong()
|
|
@@ -119,12 +168,26 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
119
168
|
|
|
120
169
|
function handle(a, b) {
|
|
121
170
|
const path = b.relation.schema + '.' + b.relation.table
|
|
171
|
+
, key = b.relation.keys.length && '=' + b.relation.keys.map(x => a[x.name])
|
|
122
172
|
call('*', a, b)
|
|
123
173
|
call('*:' + path, a, b)
|
|
124
|
-
|
|
174
|
+
key && call('*:' + path + key, a, b)
|
|
125
175
|
call(b.command, a, b)
|
|
126
176
|
call(b.command + ':' + path, a, b)
|
|
127
|
-
|
|
177
|
+
key && call(b.command + ':' + path + key, a, b)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function skip(x, error) {
|
|
181
|
+
const command = commands[x[0]]
|
|
182
|
+
, relation = command && state[x.readUInt32BE(1)]
|
|
183
|
+
|
|
184
|
+
if (!relation)
|
|
185
|
+
throw error
|
|
186
|
+
|
|
187
|
+
const path = relation.schema + '.' + relation.table
|
|
188
|
+
, events = ['*', '*:' + path, command, command + ':' + path]
|
|
189
|
+
|
|
190
|
+
report(error, event => events.includes(event.split('=')[0]))
|
|
128
191
|
}
|
|
129
192
|
|
|
130
193
|
function pong() {
|
|
@@ -137,7 +200,7 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
137
200
|
}
|
|
138
201
|
|
|
139
202
|
function call(x, a, b) {
|
|
140
|
-
subscribers.has(x) && subscribers.get(x).forEach(({ fn }) => fn
|
|
203
|
+
subscribers.has(x) && subscribers.get(x).forEach(({ fn }) => callback(fn, a, b, x))
|
|
141
204
|
}
|
|
142
205
|
}
|
|
143
206
|
|
|
@@ -162,19 +225,24 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
162
225
|
let columnIndex = 0
|
|
163
226
|
, column
|
|
164
227
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
228
|
+
try {
|
|
229
|
+
while (i < x.length) {
|
|
230
|
+
column = r.columns[columnIndex++] = {
|
|
231
|
+
key: x[i++],
|
|
232
|
+
name: transform.column.from
|
|
233
|
+
? transform.column.from(x.toString('utf8', i, i = x.indexOf(0, i)))
|
|
234
|
+
: x.toString('utf8', i, i = x.indexOf(0, i)),
|
|
235
|
+
type: x.readUInt32BE(i += 1),
|
|
236
|
+
parser: parsers[x.readUInt32BE(i)],
|
|
237
|
+
atttypmod: x.readUInt32BE(i += 4)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
column.key && r.keys.push(column)
|
|
241
|
+
i += 4
|
|
174
242
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
243
|
+
} catch (error) {
|
|
244
|
+
r.error = error
|
|
245
|
+
r.failed = true
|
|
178
246
|
}
|
|
179
247
|
},
|
|
180
248
|
Y: () => { /* noop */ }, // Type
|
|
@@ -185,7 +253,7 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
185
253
|
},
|
|
186
254
|
I: x => { // Insert
|
|
187
255
|
let i = 1
|
|
188
|
-
const relation = state[x.readUInt32BE(i)]
|
|
256
|
+
const relation = decodable(state[x.readUInt32BE(i)])
|
|
189
257
|
const { row } = tuples(x, relation.columns, i += 7, transform)
|
|
190
258
|
|
|
191
259
|
handle(row, {
|
|
@@ -195,7 +263,7 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
195
263
|
},
|
|
196
264
|
D: x => { // Delete
|
|
197
265
|
let i = 1
|
|
198
|
-
const relation = state[x.readUInt32BE(i)]
|
|
266
|
+
const relation = decodable(state[x.readUInt32BE(i)])
|
|
199
267
|
i += 4
|
|
200
268
|
const key = x[i] === 75
|
|
201
269
|
handle(key || x[i] === 79
|
|
@@ -209,7 +277,7 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
209
277
|
},
|
|
210
278
|
U: x => { // Update
|
|
211
279
|
let i = 1
|
|
212
|
-
const relation = state[x.readUInt32BE(i)]
|
|
280
|
+
const relation = decodable(state[x.readUInt32BE(i)])
|
|
213
281
|
i += 4
|
|
214
282
|
const key = x[i] === 75
|
|
215
283
|
const xs = key || x[i] === 79
|
|
@@ -232,6 +300,13 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
232
300
|
}).reduce(char, {})[x[0]](x)
|
|
233
301
|
}
|
|
234
302
|
|
|
303
|
+
function decodable(relation) {
|
|
304
|
+
if (relation.failed)
|
|
305
|
+
throw relation.error
|
|
306
|
+
|
|
307
|
+
return relation
|
|
308
|
+
}
|
|
309
|
+
|
|
235
310
|
function tuples(x, columns, xi, transform) {
|
|
236
311
|
let type
|
|
237
312
|
, column
|
|
@@ -265,7 +340,7 @@ function tuples(x, columns, xi, transform) {
|
|
|
265
340
|
}
|
|
266
341
|
|
|
267
342
|
function parseEvent(x) {
|
|
268
|
-
const xs = x.match(/^(\*|insert|update|delete)?:?([
|
|
343
|
+
const xs = x.match(/^(\*|insert|update|delete)?:?([^.=]+(?:\.[^=]+)?)?=?(.+)?/i) || []
|
|
269
344
|
|
|
270
345
|
if (!xs)
|
|
271
346
|
throw new Error('Malformed subscribe pattern: ' + x)
|