Skip to content

Commit aeb616f

Browse files
authored
Postgres: parse LIMIT after the row-locking clause (#2391)
1 parent 5e7b007 commit aeb616f

2 files changed

Lines changed: 28 additions & 1 deletion

File tree

src/parser/mod.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14481,7 +14481,7 @@ impl<'a> Parser<'a> {
1448114481

1448214482
let order_by = self.parse_optional_order_by()?;
1448314483

14484-
let limit_clause = self.parse_optional_limit_clause()?;
14484+
let mut limit_clause = self.parse_optional_limit_clause()?;
1448514485

1448614486
let settings = self.parse_settings()?;
1448714487

@@ -14501,6 +14501,16 @@ impl<'a> Parser<'a> {
1450114501
locks.push(self.parse_lock()?);
1450214502
}
1450314503
}
14504+
14505+
// Some databases (e.g. PostgreSQL) accept `LIMIT`/`OFFSET` after the
14506+
// row-locking clause (`... FOR UPDATE SKIP LOCKED LIMIT 5`) as well
14507+
// as before it. The locking clause above is parsed for every
14508+
// dialect, so accept a trailing limit here too rather than gating it
14509+
// behind a dialect flag.
14510+
if limit_clause.is_none() {
14511+
limit_clause = self.parse_optional_limit_clause()?;
14512+
}
14513+
1450414514
let format_clause =
1450514515
if self.dialect.supports_select_format() && self.parse_keyword(Keyword::FORMAT) {
1450614516
if self.parse_keyword(Keyword::NULL) {

tests/sqlparser_postgres.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9565,3 +9565,20 @@ fn exclude_as_column_name() {
95659565
}
95669566
}
95679567
}
9568+
9569+
#[test]
9570+
fn parse_limit_after_locking_clause() {
9571+
// PostgreSQL accepts `LIMIT`/`OFFSET` after the row-locking clause as well
9572+
// as before it; both orderings are semantically identical. The AST renders
9573+
// the limit in its canonical position (before the locking clause).
9574+
pg().one_statement_parses_to(
9575+
"SELECT * FROM t ORDER BY id FOR UPDATE SKIP LOCKED LIMIT 5",
9576+
"SELECT * FROM t ORDER BY id LIMIT 5 FOR UPDATE SKIP LOCKED",
9577+
);
9578+
pg().one_statement_parses_to(
9579+
"SELECT * FROM t FOR UPDATE LIMIT 5",
9580+
"SELECT * FROM t LIMIT 5 FOR UPDATE",
9581+
);
9582+
// The pre-existing ordering keeps round-tripping unchanged.
9583+
pg().verified_stmt("SELECT * FROM t ORDER BY id LIMIT 5 FOR UPDATE SKIP LOCKED");
9584+
}

0 commit comments

Comments
 (0)