Skip to content
Open
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
35 changes: 32 additions & 3 deletions pgdog/src/frontend/regex_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,17 @@ impl RegexParser {

/// Check if we should enable the parser just for this request.
pub(crate) fn use_parser(&self, request: &ClientRequest) -> bool {
let with_locks = self.level == QueryParserLevel::SessionControlAndLocks;
let session_control =
self.level == QueryParserLevel::SessionControl || self.level == QueryParserLevel::Auto;
// Auto must detect advisory locks even when nothing else needs the
// parser (single primary, no sharding): session advisory locks taken
// through a transaction-mode pool are a correctness hazard — without
// detection the client isn't pinned, the unlock can land on a
// different backend, and the lock strands on the pooled server
// connection.
let with_locks = matches!(
self.level,
QueryParserLevel::SessionControlAndLocks | QueryParserLevel::Auto
);
let session_control = self.level == QueryParserLevel::SessionControl;

if (with_locks || session_control)
&& let Ok(Some(query)) = request.query()
Expand Down Expand Up @@ -198,6 +206,27 @@ mod test {
));
}

#[test]
fn test_advisory_lock_auto_level() {
// Auto must detect advisory locks even on clusters where nothing else
// enables the parser (single primary, no sharding); otherwise session
// locks taken through a transaction-mode pool strand on pooled
// backends when the unlock routes to a different server connection.
let l = QueryParserLevel::Auto;
assert!(matches_at("SELECT pg_advisory_lock(1)", l));
assert!(matches_at("SELECT pg_try_advisory_lock(1, 2)", l));
assert!(matches_at(
"SELECT pg_try_advisory_lock(1, 2) AS t0abc /* app lock */",
l
));
assert!(matches_at("SELECT pg_advisory_unlock(1, 2)", l));
assert!(matches_at("SELECT pg_advisory_unlock_all()", l));
// Base session-control statements still match at Auto.
assert!(matches_at("NOTIFY test_channel", l));
// Plain queries still bypass the parser at Auto.
assert!(!matches_at("SELECT 1", l));
}

#[test]
fn test_scan_prefix_limit() {
let l = QueryParserLevel::SessionControlAndLocks;
Expand Down