@steadybrew/postgresjs 4.3.0 → 4.4.1
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 +59 -19
- package/cf/src/errors.js +8 -0
- package/cf/src/index.js +61 -34
- package/cf/src/subscribe.js +127 -47
- package/cjs/src/connection.js +59 -19
- package/cjs/src/errors.js +8 -0
- package/cjs/src/index.js +61 -34
- package/cjs/src/subscribe.js +127 -47
- package/package.json +1 -1
- package/src/connection.js +59 -19
- package/src/errors.js +8 -0
- package/src/index.js +61 -34
- package/src/subscribe.js +127 -47
- package/types/index.d.ts +9 -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
|
|
|
@@ -218,7 +219,7 @@ function Postgres(a, b) {
|
|
|
218
219
|
})
|
|
219
220
|
},
|
|
220
221
|
onnotify(c, x) {
|
|
221
|
-
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))
|
|
222
223
|
}
|
|
223
224
|
}))
|
|
224
225
|
|
|
@@ -228,7 +229,7 @@ function Postgres(a, b) {
|
|
|
228
229
|
if (exists) {
|
|
229
230
|
channels[name].listeners.push(listener)
|
|
230
231
|
const result = await channels[name].result
|
|
231
|
-
listener.onlisten
|
|
232
|
+
callback(listener.onlisten)
|
|
232
233
|
return { state: result.state, unlisten }
|
|
233
234
|
}
|
|
234
235
|
|
|
@@ -236,7 +237,7 @@ function Postgres(a, b) {
|
|
|
236
237
|
sql.unsafe('"' + name.replace(/"/g, '""') + '"')
|
|
237
238
|
}`, listeners: [listener] }
|
|
238
239
|
const result = await channels[name].result
|
|
239
|
-
listener.onlisten
|
|
240
|
+
callback(listener.onlisten)
|
|
240
241
|
return { state: result.state, unlisten }
|
|
241
242
|
|
|
242
243
|
async function unlisten() {
|
|
@@ -272,7 +273,9 @@ function Postgres(a, b) {
|
|
|
272
273
|
const query = { reserve: c => resolve(own(c, l)), reject }
|
|
273
274
|
closed.length
|
|
274
275
|
? connect(closed.shift(), query)
|
|
275
|
-
:
|
|
276
|
+
: allHeldByCopy()
|
|
277
|
+
? reject(copyInProgress())
|
|
278
|
+
: queries.push(query)
|
|
276
279
|
})
|
|
277
280
|
return l
|
|
278
281
|
}
|
|
@@ -294,9 +297,9 @@ function Postgres(a, b) {
|
|
|
294
297
|
function send(l, q, expected = Lease.Active) {
|
|
295
298
|
const c = l.connection
|
|
296
299
|
l.state === expected
|
|
297
|
-
? c.queue
|
|
298
|
-
? (q
|
|
299
|
-
:
|
|
300
|
+
? c.copying || (c.queue !== full && c.accepting)
|
|
301
|
+
? c.execute(q) || move(c, full)
|
|
302
|
+
: (move(c, full), q.queue = l.queue, l.queue.push(q))
|
|
300
303
|
: q.reject(leaseError(l.state))
|
|
301
304
|
}
|
|
302
305
|
|
|
@@ -352,14 +355,16 @@ function Postgres(a, b) {
|
|
|
352
355
|
}
|
|
353
356
|
|
|
354
357
|
async function finish(statement) {
|
|
358
|
+
let result
|
|
355
359
|
l.state = Lease.Settling
|
|
356
360
|
try {
|
|
357
|
-
await statement(Sql(q => send(l, q, Lease.Settling)))
|
|
361
|
+
result = await statement(Sql(q => send(l, q, Lease.Settling)))
|
|
358
362
|
} catch (e) {
|
|
359
363
|
settle(l, Lease.Discarded)
|
|
360
364
|
throw e
|
|
361
365
|
}
|
|
362
366
|
settle(l, Lease.Released)
|
|
367
|
+
return result
|
|
363
368
|
}
|
|
364
369
|
|
|
365
370
|
async function scope(fn, name) {
|
|
@@ -374,7 +379,7 @@ function Postgres(a, b) {
|
|
|
374
379
|
let uncaughtError
|
|
375
380
|
, result
|
|
376
381
|
|
|
377
|
-
name && await sql
|
|
382
|
+
name && await sql.unsafe('savepoint ' + escapeIdentifier(name))
|
|
378
383
|
try {
|
|
379
384
|
result = await new Promise((resolve, reject) => {
|
|
380
385
|
const x = fn(sql)
|
|
@@ -385,8 +390,8 @@ function Postgres(a, b) {
|
|
|
385
390
|
throw uncaughtError
|
|
386
391
|
} catch (e) {
|
|
387
392
|
l.state === Lease.Active && await (name
|
|
388
|
-
? sql
|
|
389
|
-
: finish(final => final
|
|
393
|
+
? sql.unsafe('rollback to ' + escapeIdentifier(name))
|
|
394
|
+
: finish(final => final.unsafe('rollback'))
|
|
390
395
|
)
|
|
391
396
|
throw e instanceof PostgresError && e.code === '25P02' && uncaughtError || e
|
|
392
397
|
}
|
|
@@ -394,10 +399,18 @@ function Postgres(a, b) {
|
|
|
394
399
|
if (l.state !== Lease.Active)
|
|
395
400
|
throw l.error || leaseError(l.state)
|
|
396
401
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
402
|
+
if (!name) {
|
|
403
|
+
const done = await finish(final => prepare
|
|
404
|
+
? final.unsafe('prepare transaction ' + prepare)
|
|
405
|
+
: final.unsafe('commit')
|
|
406
|
+
)
|
|
407
|
+
if (done.command === 'ROLLBACK') {
|
|
408
|
+
throw Object.assign(
|
|
409
|
+
Errors.generic('TRANSACTION_ROLLED_BACK', 'The server rolled the transaction back instead of committing it'),
|
|
410
|
+
uncaughtError && { cause: uncaughtError }
|
|
411
|
+
)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
401
414
|
|
|
402
415
|
return result
|
|
403
416
|
|
|
@@ -426,14 +439,6 @@ function Postgres(a, b) {
|
|
|
426
439
|
return c
|
|
427
440
|
}
|
|
428
441
|
|
|
429
|
-
function callback(fn, ...args) {
|
|
430
|
-
try {
|
|
431
|
-
fn && fn(...args)
|
|
432
|
-
} catch (e) {
|
|
433
|
-
queueMicrotask(() => { throw e })
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
442
|
function json(x) {
|
|
438
443
|
return new Parameter(x, 3802)
|
|
439
444
|
}
|
|
@@ -456,9 +461,32 @@ function Postgres(a, b) {
|
|
|
456
461
|
if (closed.length)
|
|
457
462
|
return connect(closed.shift(), query)
|
|
458
463
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
464
|
+
const c = takeBusy()
|
|
465
|
+
c
|
|
466
|
+
? go(c, query)
|
|
467
|
+
: allHeldByCopy()
|
|
468
|
+
? query.reject(copyInProgress())
|
|
469
|
+
: queries.push(query)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function allHeldByCopy() {
|
|
473
|
+
return connections.every(c => c.copyHeld)
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function copyInProgress() {
|
|
477
|
+
return Errors.generic('COPY_IN_PROGRESS', 'You cannot execute queries during copy')
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function takeBusy() {
|
|
481
|
+
while (busy.length) {
|
|
482
|
+
const c = busy.shift()
|
|
483
|
+
if (c.accepting)
|
|
484
|
+
return c
|
|
485
|
+
|
|
486
|
+
move(c, full)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return null
|
|
462
490
|
}
|
|
463
491
|
|
|
464
492
|
function takeOpen() {
|
|
@@ -545,6 +573,9 @@ function Postgres(a, b) {
|
|
|
545
573
|
if (initial && initial.reserve)
|
|
546
574
|
return initial.reserve(c)
|
|
547
575
|
|
|
576
|
+
if (!initial && !c.accepting)
|
|
577
|
+
return move(c, full)
|
|
578
|
+
|
|
548
579
|
if (!initial && !queries.length)
|
|
549
580
|
return move(c, open)
|
|
550
581
|
|
|
@@ -583,7 +614,7 @@ function Postgres(a, b) {
|
|
|
583
614
|
function onclose(c, e) {
|
|
584
615
|
move(c, closed)
|
|
585
616
|
c.owner && settle(c.owner, Lease.Closed, e)
|
|
586
|
-
options.onclose
|
|
617
|
+
callback(options.onclose, c.id)
|
|
587
618
|
if (ending)
|
|
588
619
|
return closedWhileEnding(c, e)
|
|
589
620
|
|
|
@@ -638,7 +669,7 @@ function parseOptions(a, b) {
|
|
|
638
669
|
connect_timeout : 30,
|
|
639
670
|
max_lifetime : max_lifetime,
|
|
640
671
|
max_pipeline : 100,
|
|
641
|
-
backoff :
|
|
672
|
+
backoff : defaultBackoff,
|
|
642
673
|
keep_alive : 60,
|
|
643
674
|
prepare : true,
|
|
644
675
|
debug : false,
|
|
@@ -697,10 +728,6 @@ function tsa(o, url, env) {
|
|
|
697
728
|
throw new Error('target_session_attrs ' + x + ' is not supported')
|
|
698
729
|
}
|
|
699
730
|
|
|
700
|
-
function backoff(retries) {
|
|
701
|
-
return (0.5 + Math.random() / 2) * Math.min(3 ** retries / 100, 20)
|
|
702
|
-
}
|
|
703
|
-
|
|
704
731
|
function max_lifetime() {
|
|
705
732
|
return 60 * (30 + Math.random() * 30)
|
|
706
733
|
}
|
package/cjs/src/subscribe.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
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' }
|
|
6
|
+
, statusInterval = 1000
|
|
2
7
|
|
|
3
8
|
module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
4
9
|
const subscribers = new Map()
|
|
@@ -7,6 +12,9 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
7
12
|
|
|
8
13
|
let connection
|
|
9
14
|
, stream
|
|
15
|
+
, timer
|
|
16
|
+
, inflight = false
|
|
17
|
+
, retries = 0
|
|
10
18
|
, ended = false
|
|
11
19
|
|
|
12
20
|
const sql = subscribe.sql = postgres({
|
|
@@ -21,13 +29,12 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
21
29
|
...options.connection,
|
|
22
30
|
replication: 'database'
|
|
23
31
|
},
|
|
24
|
-
onclose:
|
|
32
|
+
onclose: function() {
|
|
25
33
|
if (ended)
|
|
26
34
|
return
|
|
27
35
|
stream = null
|
|
28
36
|
state.pid = state.secret = undefined
|
|
29
|
-
|
|
30
|
-
subscribers.forEach(event => event.forEach(({ onsubscribe }) => onsubscribe()))
|
|
37
|
+
inflight || timer || !connection || connect().catch(noop)
|
|
31
38
|
},
|
|
32
39
|
no_subscribe: true
|
|
33
40
|
})
|
|
@@ -37,6 +44,9 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
37
44
|
|
|
38
45
|
sql.end = async() => {
|
|
39
46
|
ended = true
|
|
47
|
+
clearTimeout(timer)
|
|
48
|
+
timer = null
|
|
49
|
+
connection && await connection.catch(noop)
|
|
40
50
|
stream && (await new Promise(r => (stream.once('close', r), stream.end())))
|
|
41
51
|
return end()
|
|
42
52
|
}
|
|
@@ -48,34 +58,74 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
48
58
|
|
|
49
59
|
return subscribe
|
|
50
60
|
|
|
51
|
-
async function subscribe(event, fn, onsubscribe = noop, onerror
|
|
61
|
+
async function subscribe(event, fn, onsubscribe = noop, onerror) {
|
|
52
62
|
event = parseEvent(event)
|
|
53
63
|
|
|
54
|
-
|
|
55
|
-
connection = init(sql, slot, options.publications)
|
|
64
|
+
const subscriber = { fn, onsubscribe, onerror }
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
return (connection || connect()).then(() => {
|
|
67
|
+
const fns = subscribers.has(event)
|
|
68
|
+
? subscribers.get(event).add(subscriber)
|
|
69
|
+
: subscribers.set(event, new Set([subscriber])).get(event)
|
|
61
70
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
const unsubscribe = () => {
|
|
72
|
+
fns.delete(subscriber)
|
|
73
|
+
fns.size === 0 && subscribers.delete(event)
|
|
74
|
+
subscribers.size === 0 && (clearTimeout(timer), timer = null)
|
|
75
|
+
}
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
connected(x)
|
|
69
|
-
onsubscribe()
|
|
70
|
-
stream && stream.on('error', onerror)
|
|
77
|
+
callback(onsubscribe)
|
|
71
78
|
return { unsubscribe, state, sql }
|
|
72
79
|
})
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
function connect() {
|
|
83
|
+
clearTimeout(timer)
|
|
84
|
+
timer = null
|
|
85
|
+
inflight = true
|
|
86
|
+
const resume = subscribers.size > 0
|
|
87
|
+
const current = connection = init(sql, slot, options.publications).then(x => {
|
|
88
|
+
stream = x.stream
|
|
89
|
+
state.pid = x.state.pid
|
|
90
|
+
state.secret = x.state.secret
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
current.then(
|
|
94
|
+
() => {
|
|
95
|
+
inflight = false
|
|
96
|
+
retries = 0
|
|
97
|
+
resume && !ended && subscribers.forEach(event => event.forEach(({ onsubscribe }) => callback(onsubscribe)))
|
|
98
|
+
},
|
|
99
|
+
error => {
|
|
100
|
+
inflight = false
|
|
101
|
+
connection === current && (connection = null)
|
|
102
|
+
if (ended || subscribers.size === 0)
|
|
103
|
+
return
|
|
104
|
+
reportConnection(error)
|
|
105
|
+
timer = setTimeout(() => connect().catch(noop), retryDelay(retries++))
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return current
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function retryDelay(n) {
|
|
113
|
+
let seconds = backoffSeconds(sql.options.backoff, n)
|
|
114
|
+
seconds = Number.isFinite(seconds) ? seconds : defaultBackoff(n)
|
|
115
|
+
return Math.min(Math.max(seconds * 1000, 100), 2 ** 31 - 1)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function reportConnection(error) {
|
|
119
|
+
console.error('Unexpected error during logical streaming - reconnecting', error) // eslint-disable-line
|
|
120
|
+
subscribers.forEach(fns => fns.forEach(({ onerror }) => callback(onerror, error)))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function report(error, match) {
|
|
124
|
+
let uncaught = false
|
|
125
|
+
subscribers.forEach((fns, event) => match(event) && fns.forEach(({ onerror }) =>
|
|
126
|
+
onerror ? callback(onerror, error) : uncaught = true
|
|
127
|
+
))
|
|
128
|
+
uncaught && callback(() => { throw error })
|
|
79
129
|
}
|
|
80
130
|
|
|
81
131
|
async function init(sql, slot, publications) {
|
|
@@ -99,18 +149,22 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
99
149
|
}
|
|
100
150
|
|
|
101
151
|
stream.on('data', data)
|
|
102
|
-
stream.on('error',
|
|
152
|
+
stream.on('error', reportConnection)
|
|
103
153
|
stream.on('close', sql.close)
|
|
104
154
|
|
|
105
|
-
|
|
155
|
+
// A walsender can miss the wakeup for a commit and sleep until its timeout; a status update wakes it.
|
|
156
|
+
const status = setInterval(() => stream.writable && pong(), statusInterval)
|
|
157
|
+
stream.on('close', () => clearInterval(status))
|
|
106
158
|
|
|
107
|
-
|
|
108
|
-
console.error('Unexpected error during logical streaming - reconnecting', e) // eslint-disable-line
|
|
109
|
-
}
|
|
159
|
+
return { stream, state: xs.state }
|
|
110
160
|
|
|
111
161
|
function data(x) {
|
|
112
162
|
if (x[0] === 0x77) {
|
|
113
|
-
|
|
163
|
+
try {
|
|
164
|
+
parse(x.subarray(25), state, sql.options.parsers, handle, options.transform)
|
|
165
|
+
} catch (error) {
|
|
166
|
+
skip(x.subarray(25), error)
|
|
167
|
+
}
|
|
114
168
|
} else if (x[0] === 0x6b && x[17]) {
|
|
115
169
|
state.lsn = x.subarray(1, 9)
|
|
116
170
|
pong()
|
|
@@ -119,12 +173,26 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
119
173
|
|
|
120
174
|
function handle(a, b) {
|
|
121
175
|
const path = b.relation.schema + '.' + b.relation.table
|
|
176
|
+
, key = b.relation.keys.length && '=' + b.relation.keys.map(x => a[x.name])
|
|
122
177
|
call('*', a, b)
|
|
123
178
|
call('*:' + path, a, b)
|
|
124
|
-
|
|
179
|
+
key && call('*:' + path + key, a, b)
|
|
125
180
|
call(b.command, a, b)
|
|
126
181
|
call(b.command + ':' + path, a, b)
|
|
127
|
-
|
|
182
|
+
key && call(b.command + ':' + path + key, a, b)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function skip(x, error) {
|
|
186
|
+
const command = commands[x[0]]
|
|
187
|
+
, relation = command && state[x.readUInt32BE(1)]
|
|
188
|
+
|
|
189
|
+
if (!relation)
|
|
190
|
+
throw error
|
|
191
|
+
|
|
192
|
+
const path = relation.schema + '.' + relation.table
|
|
193
|
+
, events = ['*', '*:' + path, command, command + ':' + path]
|
|
194
|
+
|
|
195
|
+
report(error, event => events.includes(event.split('=')[0]))
|
|
128
196
|
}
|
|
129
197
|
|
|
130
198
|
function pong() {
|
|
@@ -137,7 +205,7 @@ module.exports = Subscribe;function Subscribe(postgres, options) {
|
|
|
137
205
|
}
|
|
138
206
|
|
|
139
207
|
function call(x, a, b) {
|
|
140
|
-
subscribers.has(x) && subscribers.get(x).forEach(({ fn }) => fn
|
|
208
|
+
subscribers.has(x) && subscribers.get(x).forEach(({ fn }) => callback(fn, a, b, x))
|
|
141
209
|
}
|
|
142
210
|
}
|
|
143
211
|
|
|
@@ -162,19 +230,24 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
162
230
|
let columnIndex = 0
|
|
163
231
|
, column
|
|
164
232
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
233
|
+
try {
|
|
234
|
+
while (i < x.length) {
|
|
235
|
+
column = r.columns[columnIndex++] = {
|
|
236
|
+
key: x[i++],
|
|
237
|
+
name: transform.column.from
|
|
238
|
+
? transform.column.from(x.toString('utf8', i, i = x.indexOf(0, i)))
|
|
239
|
+
: x.toString('utf8', i, i = x.indexOf(0, i)),
|
|
240
|
+
type: x.readUInt32BE(i += 1),
|
|
241
|
+
parser: parsers[x.readUInt32BE(i)],
|
|
242
|
+
atttypmod: x.readUInt32BE(i += 4)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
column.key && r.keys.push(column)
|
|
246
|
+
i += 4
|
|
174
247
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
248
|
+
} catch (error) {
|
|
249
|
+
r.error = error
|
|
250
|
+
r.failed = true
|
|
178
251
|
}
|
|
179
252
|
},
|
|
180
253
|
Y: () => { /* noop */ }, // Type
|
|
@@ -185,7 +258,7 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
185
258
|
},
|
|
186
259
|
I: x => { // Insert
|
|
187
260
|
let i = 1
|
|
188
|
-
const relation = state[x.readUInt32BE(i)]
|
|
261
|
+
const relation = decodable(state[x.readUInt32BE(i)])
|
|
189
262
|
const { row } = tuples(x, relation.columns, i += 7, transform)
|
|
190
263
|
|
|
191
264
|
handle(row, {
|
|
@@ -195,7 +268,7 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
195
268
|
},
|
|
196
269
|
D: x => { // Delete
|
|
197
270
|
let i = 1
|
|
198
|
-
const relation = state[x.readUInt32BE(i)]
|
|
271
|
+
const relation = decodable(state[x.readUInt32BE(i)])
|
|
199
272
|
i += 4
|
|
200
273
|
const key = x[i] === 75
|
|
201
274
|
handle(key || x[i] === 79
|
|
@@ -209,7 +282,7 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
209
282
|
},
|
|
210
283
|
U: x => { // Update
|
|
211
284
|
let i = 1
|
|
212
|
-
const relation = state[x.readUInt32BE(i)]
|
|
285
|
+
const relation = decodable(state[x.readUInt32BE(i)])
|
|
213
286
|
i += 4
|
|
214
287
|
const key = x[i] === 75
|
|
215
288
|
const xs = key || x[i] === 79
|
|
@@ -232,6 +305,13 @@ function parse(x, state, parsers, handle, transform) {
|
|
|
232
305
|
}).reduce(char, {})[x[0]](x)
|
|
233
306
|
}
|
|
234
307
|
|
|
308
|
+
function decodable(relation) {
|
|
309
|
+
if (relation.failed)
|
|
310
|
+
throw relation.error
|
|
311
|
+
|
|
312
|
+
return relation
|
|
313
|
+
}
|
|
314
|
+
|
|
235
315
|
function tuples(x, columns, xi, transform) {
|
|
236
316
|
let type
|
|
237
317
|
, column
|
|
@@ -265,7 +345,7 @@ function tuples(x, columns, xi, transform) {
|
|
|
265
345
|
}
|
|
266
346
|
|
|
267
347
|
function parseEvent(x) {
|
|
268
|
-
const xs = x.match(/^(\*|insert|update|delete)?:?([
|
|
348
|
+
const xs = x.match(/^(\*|insert|update|delete)?:?([^.=]+(?:\.[^=]+)?)?=?(.+)?/i) || []
|
|
269
349
|
|
|
270
350
|
if (!xs)
|
|
271
351
|
throw new Error('Malformed subscribe pattern: ' + x)
|