Skip to content

Commit 4e28de9

Browse files
committed
Support transform in subscribe messages
1 parent 2fd6edd commit 4e28de9

File tree

2 files changed

+78
-24
lines changed

2 files changed

+78
-24
lines changed

src/subscribe.js

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export default function Subscribe(postgres, options) {
104104

105105
function data(x) {
106106
if (x[0] === 0x77)
107-
parse(x.subarray(25), state, sql.options.parsers, handle)
107+
parse(x.subarray(25), state, sql.options.parsers, handle, options.transform)
108108
else if (x[0] === 0x6b && x[17])
109109
pong()
110110
}
@@ -137,15 +137,15 @@ function Time(x) {
137137
return new Date(Date.UTC(2000, 0, 1) + Number(x / BigInt(1000)))
138138
}
139139

140-
function parse(x, state, parsers, handle) {
140+
function parse(x, state, parsers, handle, transform) {
141141
const char = (acc, [k, v]) => (acc[k.charCodeAt(0)] = v, acc)
142142

143143
Object.entries({
144144
R: x => { // Relation
145145
let i = 1
146146
const r = state[x.readUInt32BE(i)] = {
147-
schema: String(x.subarray(i += 4, i = x.indexOf(0, i))) || 'pg_catalog',
148-
table: String(x.subarray(i + 1, i = x.indexOf(0, i + 1))),
147+
schema: x.toString('utf8', i += 4, i = x.indexOf(0, i)) || 'pg_catalog',
148+
table: x.toString('utf8', i + 1, i = x.indexOf(0, i + 1)),
149149
columns: Array(x.readUInt16BE(i += 2)),
150150
keys: []
151151
}
@@ -157,7 +157,9 @@ function parse(x, state, parsers, handle) {
157157
while (i < x.length) {
158158
column = r.columns[columnIndex++] = {
159159
key: x[i++],
160-
name: String(x.subarray(i, i = x.indexOf(0, i))),
160+
name: transform.column.from
161+
? transform.column.from(x.toString('utf8', i, i = x.indexOf(0, i)))
162+
: x.toString('utf8', i, i = x.indexOf(0, i)),
161163
type: x.readUInt32BE(i += 1),
162164
parser: parsers[x.readUInt32BE(i)],
163165
atttypmod: x.readUInt32BE(i += 4)
@@ -176,8 +178,7 @@ function parse(x, state, parsers, handle) {
176178
I: x => { // Insert
177179
let i = 1
178180
const relation = state[x.readUInt32BE(i)]
179-
const row = {}
180-
tuples(x, row, relation.columns, i += 7)
181+
const { row } = tuples(x, relation.columns, i += 7, transform)
181182

182183
handle(row, {
183184
command: 'insert',
@@ -189,13 +190,10 @@ function parse(x, state, parsers, handle) {
189190
const relation = state[x.readUInt32BE(i)]
190191
i += 4
191192
const key = x[i] === 75
192-
const row = key || x[i] === 79
193-
? {}
193+
handle(key || x[i] === 79
194+
? tuples(x, key ? relation.keys : relation.columns, i += 3, transform).row
194195
: null
195-
196-
tuples(x, row, key ? relation.keys : relation.columns, i += 3)
197-
198-
handle(row, {
196+
, {
199197
command: 'delete',
200198
relation,
201199
key
@@ -206,35 +204,36 @@ function parse(x, state, parsers, handle) {
206204
const relation = state[x.readUInt32BE(i)]
207205
i += 4
208206
const key = x[i] === 75
209-
const old = key || x[i] === 79
210-
? {}
207+
const xs = key || x[i] === 79
208+
? tuples(x, key ? relation.keys : relation.columns, i += 3, transform)
211209
: null
212210

213-
old && (i = tuples(x, old, key ? relation.keys : relation.columns, i += 3))
211+
xs && (i = xs.i)
214212

215-
const row = {}
216-
tuples(x, row, relation.columns, i + 3)
213+
const { row } = tuples(x, relation.columns, i + 3, transform)
217214

218215
handle(row, {
219216
command: 'update',
220217
relation,
221218
key,
222-
old
219+
old: xs && xs.row
223220
})
224221
},
225222
T: () => { /* noop */ }, // Truncate,
226223
C: () => { /* noop */ } // Commit
227224
}).reduce(char, {})[x[0]](x)
228225
}
229226

230-
function tuples(x, row, columns, xi) {
227+
function tuples(x, columns, xi, transform) {
231228
let type
232229
, column
230+
, value
233231

232+
const row = transform.raw ? new Array(columns.length) : {}
234233
for (let i = 0; i < columns.length; i++) {
235234
type = x[xi++]
236235
column = columns[i]
237-
row[column.name] = type === 110 // n
236+
value = type === 110 // n
238237
? null
239238
: type === 117 // u
240239
? undefined
@@ -243,9 +242,18 @@ function tuples(x, row, columns, xi) {
243242
: column.parser.array === true
244243
? column.parser(x.toString('utf8', xi + 5, xi += 4 + x.readUInt32BE(xi)))
245244
: column.parser(x.toString('utf8', xi + 4, xi += 4 + x.readUInt32BE(xi)))
245+
246+
transform.raw
247+
? (row[i] = transform.raw === true
248+
? value
249+
: transform.value.from ? transform.value.from(value, column) : value)
250+
: (row[column.name] = transform.value.from
251+
? transform.value.from(value, column)
252+
: value
253+
)
246254
}
247255

248-
return xi
256+
return { i: xi, row: transform.row.from ? transform.row.from(row) : row }
249257
}
250258

251259
function parseEvent(x) {

tests/index.js

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,8 +1859,7 @@ t('multiple queries before connect', async() => {
18591859
t('subscribe', { timeout: 2 }, async() => {
18601860
const sql = postgres({
18611861
database: 'postgres_js_test',
1862-
publications: 'alltables',
1863-
fetch_types: false
1862+
publications: 'alltables'
18641863
})
18651864

18661865
await sql.unsafe('create publication alltables for all tables')
@@ -1899,6 +1898,53 @@ t('subscribe', { timeout: 2 }, async() => {
18991898
]
19001899
})
19011900

1901+
t('subscribe with transform', { timeout: 2 }, async() => {
1902+
const sql = postgres({
1903+
transform: {
1904+
column: {
1905+
from: postgres.toCamel,
1906+
to: postgres.fromCamel
1907+
}
1908+
},
1909+
database: 'postgres_js_test',
1910+
publications: 'alltables'
1911+
})
1912+
1913+
await sql.unsafe('create publication alltables for all tables')
1914+
1915+
const result = []
1916+
1917+
const { unsubscribe } = await sql.subscribe('*', (row, { command, old }) =>
1918+
result.push(command, row.nameInCamel || row.id, old && old.nameInCamel)
1919+
)
1920+
1921+
await sql`
1922+
create table test (
1923+
id serial primary key,
1924+
name_in_camel text
1925+
)
1926+
`
1927+
1928+
await sql`insert into test (name_in_camel) values ('Murray')`
1929+
await sql`update test set name_in_camel = 'Rothbard'`
1930+
await sql`delete from test`
1931+
await sql`alter table test replica identity full`
1932+
await sql`insert into test (name_in_camel) values ('Murray')`
1933+
await sql`update test set name_in_camel = 'Rothbard'`
1934+
await sql`delete from test`
1935+
await delay(10)
1936+
await unsubscribe()
1937+
await sql`insert into test (name_in_camel) values ('Oh noes')`
1938+
await delay(10)
1939+
return [
1940+
'insert,Murray,,update,Rothbard,,delete,1,,insert,Murray,,update,Rothbard,Murray,delete,Rothbard,',
1941+
result.join(','),
1942+
await sql`drop table test`,
1943+
await sql`drop publication alltables`,
1944+
await sql.end()
1945+
]
1946+
})
1947+
19021948
t('subscribe reconnects and calls onsubscribe', { timeout: 4 }, async() => {
19031949
const sql = postgres({
19041950
database: 'postgres_js_test',

0 commit comments

Comments
 (0)