Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions packages/pglite/src/pglite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1108,7 +1108,16 @@ export class PGlite
}
} else if (msg instanceof NotificationResponseMessage) {
// We've received a notification, call the listeners
const listeners = this.#notifyListeners.get(msg.channel)
// Listeners are stored with their original channel name. Use the
// normalized form (lowercased for unquoted identifiers) as a fallback
// so that pg.listen('TinyBase') + pg_notify('TinyBase') match.
let listeners = this.#notifyListeners.get(msg.channel)
if (!listeners) {
const pgChannel = pglUtils.toPostgresName(msg.channel)
if (pgChannel !== msg.channel) {
listeners = this.#notifyListeners.get(pgChannel)
}
}
if (listeners) {
listeners.forEach((cb) => {
// We use queueMicrotask so that the callback is called after any
Expand Down Expand Up @@ -1192,7 +1201,7 @@ export class PGlite
}
this.#notifyListeners.get(pgChannel)!.add(callback)
try {
await pg.exec(`LISTEN ${channel}`)
await pg.exec(`LISTEN ${pgChannel}`)
} catch (e) {
this.#notifyListeners.get(pgChannel)!.delete(callback)
if (this.#notifyListeners.get(pgChannel)?.size === 0) {
Expand All @@ -1201,7 +1210,7 @@ export class PGlite
throw e
}
return async (tx?: Transaction) => {
await this.unlisten(pgChannel, callback, tx)
await this.unlisten(channel, callback, tx)
}
}

Expand All @@ -1226,7 +1235,7 @@ export class PGlite
const pgChannel = pglUtils.toPostgresName(channel)
const pg = tx ?? this
const cleanUp = async () => {
await pg.exec(`UNLISTEN ${channel}`)
await pg.exec(`UNLISTEN ${pgChannel}`)
// While that query was running, another query might have subscribed
// so we need to check again
if (this.#notifyListeners.get(pgChannel)?.size === 0) {
Expand Down
11 changes: 11 additions & 0 deletions packages/pglite/tests/notify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ describe('notify API', () => {
await pg.listen('postgresdefaultlower', allLower1)
await pg.exec(`NOTIFY postgresdefaultlower, 'payload1'`)

// TinyBase: mixed-case channel names with pg_notify should match
// Regression test for https://github.com/electric-sql/pglite/issues/642
const tinyBaseListener = vi.fn()
await pg.listen('TinyBase', tinyBaseListener)
// pg_notify sends the string as-is; LISTEN uses the normalized (lowercased) name.
// The notification lookup now falls back to the normalized key, so TinyBase works.
await pg.exec(`SELECT pg_notify('TinyBase', 'hello-tinybase')`)

const autoLowerTest1 = vi.fn()
await pg.listen('PostgresDefaultLower', autoLowerTest1)
await pg.exec(`NOTIFY PostgresDefaultLower, 'payload1'`)
Expand Down Expand Up @@ -116,6 +124,9 @@ describe('notify API', () => {
expect(otherCharsWithQuotes).toHaveBeenCalledOnce()
expect(quotedWithSpaces).toHaveBeenCalledOnce()
expect(unquotedWithSpaces).not.toHaveBeenCalled()
// TinyBase regression test: pg_notify('TinyBase') must reach pg.listen('TinyBase')
expect(tinyBaseListener).toHaveBeenCalledTimes(1)
expect(tinyBaseListener).toHaveBeenCalledWith('hello-tinybase')
})

it('check unlisten case sensitivity + special chars as Postgresql', async () => {
Expand Down