Skip to content

Postgres: parse LIMIT after the row-locking clause#2391

Open
truffle-dev wants to merge 2 commits into
apache:mainfrom
truffle-dev:pg-limit-after-locking
Open

Postgres: parse LIMIT after the row-locking clause#2391
truffle-dev wants to merge 2 commits into
apache:mainfrom
truffle-dev:pg-limit-after-locking

Conversation

@truffle-dev

Copy link
Copy Markdown

PostgreSQL accepts LIMIT/OFFSET both before and after the row-locking clause, but the parser only consumed LIMIT before the lock loop, so:

SELECT * FROM t ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 5;

failed with Expected: end of statement, found: LIMIT.

This adds a supports_limit_after_locking_clause dialect method (off by default, enabled for Postgres) and parses a trailing LIMIT clause after the locks when no leading one was present. The AST renders the limit in its canonical position, so the input round-trips to ... LIMIT 5 FOR UPDATE SKIP LOCKED.

Fixes #2194.

PostgreSQL accepts LIMIT/OFFSET both before and after the row-locking
clause (FOR UPDATE/FOR SHARE/...), but the parser only consumed LIMIT
before the lock loop, so a query like

  SELECT * FROM t ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 5

failed with "Expected: end of statement, found: LIMIT".

Add a supports_limit_after_locking_clause dialect method (off by
default, on for Postgres) and parse a trailing LIMIT clause after the
locks when no leading one was present.
Comment thread src/parser/mod.rs Outdated

// PostgreSQL accepts `LIMIT`/`OFFSET` after the row-locking clause
// (e.g. `... FOR UPDATE SKIP LOCKED LIMIT 5`) as well as before it.
if limit_clause.is_none() && self.dialect.supports_limit_after_locking_clause() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if limit_clause.is_none() && self.dialect.supports_limit_after_locking_clause() {
if limit_clause.is_none() {

I think we can skip the dialect flag entirely here, since the FOR UPDATE isn't dialect guarded either?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed - the lock loop above isn't dialect-guarded, so gating only the trailing limit was inconsistent. Dropped supports_limit_after_locking_clause and its Postgres override and made the condition just limit_clause.is_none(). Full suite (all dialect test files + doctests) and clippy are green. c1c4b70

The row-locking clause (FOR UPDATE/FOR SHARE/...) just above is parsed
for every dialect, so gating only the trailing LIMIT/OFFSET behind a
dialect flag was inconsistent. Parse it unconditionally instead and
remove supports_limit_after_locking_clause.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parsing Error for Supported query in Postgres

2 participants