-
Notifications
You must be signed in to change notification settings - Fork 235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PostgreSQL case sensitivity in listen #530
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
8af6013
added test for issue 529; quoted channel for LISTEN in pglite worker
4fc0f0e
pretty
5961051
case sensitivity tests and fixes
cf3e51f
style fix
5784b6c
better check all at the end
369cb17
more thorough testing
1c41ad1
unlisten fix and tests
850ec93
changeset
bd8598b
api made clear
4c266db
stylecheck
a312a1f
nicer
f32910b
unlisten test fix
cb70d90
test spaces and other special chars
e2574bf
style
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@electric-sql/pglite': patch | ||
--- | ||
|
||
listen and unlisten case sensitivity behaviour aligned to default PostgreSQL behaviour' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { describe, it, expect } from 'vitest' | ||
import { describe, it, expect, vi } from 'vitest' | ||
import { PGlite } from '../dist/index.js' | ||
import { expectToThrowAsync } from './test-utils.js' | ||
|
||
describe('notify API', () => { | ||
it('notify', async () => { | ||
|
@@ -41,4 +42,128 @@ describe('notify API', () => { | |
|
||
await new Promise((resolve) => setTimeout(resolve, 1000)) | ||
}) | ||
|
||
it('check notify case sensitivity + special chars as Postgresql', async () => { | ||
const pg = new PGlite() | ||
|
||
const allLower1 = vi.fn() | ||
await pg.listen('postgresdefaultlower', allLower1) | ||
await pg.query(`NOTIFY postgresdefaultlower, 'payload1'`) | ||
|
||
const autoLowerTest1 = vi.fn() | ||
await pg.listen('PostgresDefaultLower', autoLowerTest1) | ||
await pg.query(`NOTIFY PostgresDefaultLower, 'payload1'`) | ||
|
||
const autoLowerTest2 = vi.fn() | ||
await pg.listen('PostgresDefaultLower', autoLowerTest2) | ||
await pg.query(`NOTIFY postgresdefaultlower, 'payload1'`) | ||
|
||
const autoLowerTest3 = vi.fn() | ||
await pg.listen('postgresdefaultlower', autoLowerTest3) | ||
await pg.query(`NOTIFY PostgresDefaultLower, 'payload1'`) | ||
|
||
const caseSensitive1 = vi.fn() | ||
await pg.listen('"tesT2"', caseSensitive1) | ||
await pg.query(`NOTIFY "tesT2", 'paYloAd2'`) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to add a tests with spaces in the channel name: const quotedWithSpaces = vi.fn()
await pg.listen('"Quoted Channel With Spaces"', quotedWithSpaces)
await pg.query(`NOTIFY ""Quoted Channel With Spaces", 'payload1'`)
const unquotedWithSpaces = vi.fn()
await pg.listen('Unquoted Channel With Spaces', unquotedWithSpaces)
// This one may need to throw?
await pg.query(`NOTIFY "Unquoted Channel With Spaces", 'payload1'`) |
||
const caseSensitive2 = vi.fn() | ||
await pg.listen('"tesT3"', caseSensitive2) | ||
await pg.query(`NOTIFY tesT3, 'paYloAd2'`) | ||
|
||
const caseSensitive3 = vi.fn() | ||
await pg.listen('testNotCalled2', caseSensitive3) | ||
await pg.query(`NOTIFY "testNotCalled2", 'paYloAd2'`) | ||
|
||
const quotedWithSpaces = vi.fn() | ||
await pg.listen('"Quoted Channel With Spaces"', quotedWithSpaces) | ||
await pg.query(`NOTIFY "Quoted Channel With Spaces", 'payload1'`) | ||
|
||
const unquotedWithSpaces = vi.fn() | ||
await expectToThrowAsync( | ||
pg.listen('Unquoted Channel With Spaces', unquotedWithSpaces), | ||
) | ||
await expectToThrowAsync( | ||
pg.query(`NOTIFY Unquoted Channel With Spaces, 'payload1'`), | ||
) | ||
|
||
const otherCharsWithQuotes = vi.fn() | ||
await pg.listen('"test&me"', otherCharsWithQuotes) | ||
await pg.query(`NOTIFY "test&me", 'paYloAd2'`) | ||
|
||
const otherChars = vi.fn() | ||
await expectToThrowAsync(pg.listen('test&me', otherChars)) | ||
await expectToThrowAsync(pg.query(`NOTIFY test&me, 'payload1'`)) | ||
|
||
expect(allLower1).toHaveBeenCalledTimes(4) | ||
expect(autoLowerTest1).toHaveBeenCalledTimes(3) | ||
expect(autoLowerTest2).toHaveBeenCalledTimes(2) | ||
expect(autoLowerTest3).toHaveBeenCalledTimes(1) | ||
expect(caseSensitive1).toHaveBeenCalledOnce() | ||
expect(caseSensitive2).not.toHaveBeenCalled() | ||
expect(caseSensitive3).not.toHaveBeenCalled() | ||
expect(otherCharsWithQuotes).toHaveBeenCalledOnce() | ||
expect(quotedWithSpaces).toHaveBeenCalledOnce() | ||
expect(unquotedWithSpaces).not.toHaveBeenCalled() | ||
}) | ||
|
||
it('check unlisten case sensitivity + special chars as Postgresql', async () => { | ||
const pg = new PGlite() | ||
|
||
const allLower1 = vi.fn() | ||
{ | ||
const unsub1 = await pg.listen('postgresdefaultlower', allLower1) | ||
await pg.query(`NOTIFY postgresdefaultlower, 'payload1'`) | ||
await unsub1() | ||
} | ||
|
||
const autoLowerTest1 = vi.fn() | ||
{ | ||
const unsub2 = await pg.listen('PostgresDefaultLower', autoLowerTest1) | ||
await pg.query(`NOTIFY PostgresDefaultLower, 'payload1'`) | ||
await unsub2() | ||
} | ||
|
||
const autoLowerTest2 = vi.fn() | ||
{ | ||
const unsub3 = await pg.listen('PostgresDefaultLower', autoLowerTest2) | ||
await pg.query(`NOTIFY postgresdefaultlower, 'payload1'`) | ||
await unsub3() | ||
} | ||
|
||
const autoLowerTest3 = vi.fn() | ||
{ | ||
const unsub4 = await pg.listen('postgresdefaultlower', autoLowerTest3) | ||
await pg.query(`NOTIFY PostgresDefaultLower, 'payload1'`) | ||
await unsub4() | ||
} | ||
|
||
const caseSensitive1 = vi.fn() | ||
{ | ||
await pg.listen('"CaSESEnsiTIvE"', caseSensitive1) | ||
await pg.query(`NOTIFY "CaSESEnsiTIvE", 'payload1'`) | ||
await pg.unlisten('"CaSESEnsiTIvE"') | ||
await pg.query(`NOTIFY "CaSESEnsiTIvE", 'payload1'`) | ||
} | ||
|
||
const quotedWithSpaces = vi.fn() | ||
{ | ||
await pg.listen('"Quoted Channel With Spaces"', quotedWithSpaces) | ||
await pg.query(`NOTIFY "Quoted Channel With Spaces", 'payload1'`) | ||
await pg.unlisten('"Quoted Channel With Spaces"') | ||
} | ||
|
||
const otherCharsWithQuotes = vi.fn() | ||
{ | ||
await pg.listen('"test&me"', otherCharsWithQuotes) | ||
await pg.query(`NOTIFY "test&me", 'payload'`) | ||
await pg.unlisten('"test&me"') | ||
} | ||
|
||
expect(allLower1).toHaveBeenCalledOnce() | ||
expect(autoLowerTest1).toHaveBeenCalledOnce() | ||
expect(autoLowerTest2).toHaveBeenCalledOnce() | ||
expect(autoLowerTest3).toHaveBeenCalledOnce() | ||
expect(caseSensitive1).toHaveBeenCalledOnce() | ||
expect(otherCharsWithQuotes).toHaveBeenCalledOnce() | ||
}) | ||
}) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fear this breaks channel names with spaces.
You spotted in my original implementation I made a mistake, on the same thread I quoted the channel name, with the worker it was unquoted.
I think it's now apparent we need to decide between two options:
Always quote the channel name - this forces it to be case sensitive, but means that channel names with spaces will always work:
pg.listen('Channel With Spaces', ...)
Never use quotes and user has to provide them to get the case sensitivity or use spaces:
pg.listen('"Channel With Spaces"', ...)
workspg.listen('Channel With Spaces', ...)
throws an errorI'm not sure what the best option is.
For a reference point, Electric requires the user to provide the quotes to be case sensitive, and defaults to case insensitive.
Thinking about this now, I fear we may have a similar problem in the
live.incrementalQuery
api with thekey
column argument...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gonna take a moment to have a look at the possibilities. Will let you know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A reason to throw if no quotes provided:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I guess it's not only about spaces, but other characters as well - in an unquoted string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I think we should take the opportunity to consider what the correct pattern is here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More issues surfaced: if the provided
channel name
causes the db engine to throw for whatever reason, there's no cleanup of the#notifyListeners
made in the frontend. This might be the case in other parts of the code as well.