Skip to content

Commit 47a3e5e

Browse files
committed
Parse Postgres's LOCK TABLE statement
See: https://www.postgresql.org/docs/current/sql-lock.html PG's full syntax for this statement is supported: ``` LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ] where lockmode is one of: ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE ``` It is implemented to not intefere with the roughly equivalent (but different) syntax in MySQL, by using a new Statement variant.
1 parent 4b191a6 commit 47a3e5e

File tree

4 files changed

+153
-1
lines changed

4 files changed

+153
-1
lines changed

src/ast/mod.rs

+73
Original file line numberDiff line numberDiff line change
@@ -3336,6 +3336,27 @@ pub enum Statement {
33363336
is_eq: bool,
33373337
},
33383338
/// ```sql
3339+
/// LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ]
3340+
/// ```
3341+
/// Where *lockmode* is one of:
3342+
///
3343+
/// ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE
3344+
/// | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE
3345+
///
3346+
/// Note: this is a Postgres-specific statement. See <https://www.postgresql.org/docs/current/sql-lock.html>
3347+
LockTablesPG {
3348+
/// whether the TABLE keyword was present
3349+
keyword_table: bool,
3350+
/// whether the ONLY keyword was present
3351+
keyword_only: bool,
3352+
/// the tables to lock, in locking order
3353+
tables: Vec<ObjectName>,
3354+
/// the lock mode
3355+
lock_mode: Option<LockMode>,
3356+
/// whether NOWAIT keyword was present
3357+
keyword_nowait: bool,
3358+
},
3359+
/// ```sql
33393360
/// LOCK TABLES <table_name> [READ [LOCAL] | [LOW_PRIORITY] WRITE]
33403361
/// ```
33413362
/// Note: this is a MySQL-specific statement. See <https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html>
@@ -4894,6 +4915,29 @@ impl fmt::Display for Statement {
48944915
}
48954916
Ok(())
48964917
}
4918+
Statement::LockTablesPG {
4919+
keyword_table,
4920+
keyword_only,
4921+
tables,
4922+
lock_mode,
4923+
keyword_nowait,
4924+
} => {
4925+
write!(f, "LOCK ")?;
4926+
if *keyword_table {
4927+
write!(f, "TABLE ")?;
4928+
}
4929+
if *keyword_only {
4930+
write!(f, "ONLY ")?;
4931+
}
4932+
write!(f, "{}", display_comma_separated(tables))?;
4933+
if let Some(ref lock_mode) = lock_mode {
4934+
write!(f, " IN {} MODE", lock_mode)?;
4935+
}
4936+
if *keyword_nowait {
4937+
write!(f, " NOWAIT")?;
4938+
}
4939+
Ok(())
4940+
}
48974941
Statement::LockTables { tables } => {
48984942
write!(f, "LOCK TABLES {}", display_comma_separated(tables))
48994943
}
@@ -7299,6 +7343,35 @@ impl fmt::Display for LockTableType {
72997343
}
73007344
}
73017345

7346+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
7347+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
7348+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
7349+
pub enum LockMode {
7350+
AccessShare,
7351+
RowShare,
7352+
RowExclusive,
7353+
ShareUpdateExclusive,
7354+
Share,
7355+
ShareRowExclusive,
7356+
Exclusive,
7357+
AccessExclusive,
7358+
}
7359+
7360+
impl fmt::Display for LockMode {
7361+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7362+
match self {
7363+
LockMode::AccessShare => write!(f, "ACCESS SHARE"),
7364+
LockMode::RowShare => write!(f, "ROW SHARE"),
7365+
LockMode::RowExclusive => write!(f, "ROW EXCLUSIVE"),
7366+
LockMode::ShareUpdateExclusive => write!(f, "SHARE UPDATE EXCLUSIVE"),
7367+
LockMode::Share => write!(f, "SHARE"),
7368+
LockMode::ShareRowExclusive => write!(f, "SHARE ROW EXCLUSIVE"),
7369+
LockMode::Exclusive => write!(f, "EXCLUSIVE"),
7370+
LockMode::AccessExclusive => write!(f, "ACCESS EXCLUSIVE"),
7371+
}
7372+
}
7373+
}
7374+
73027375
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
73037376
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
73047377
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

src/ast/spans.rs

+1
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ impl Spanned for Statement {
478478
Statement::CreateType { .. } => Span::empty(),
479479
Statement::Pragma { .. } => Span::empty(),
480480
Statement::LockTables { .. } => Span::empty(),
481+
Statement::LockTablesPG { .. } => Span::empty(),
481482
Statement::UnlockTables => Span::empty(),
482483
Statement::Unload { .. } => Span::empty(),
483484
Statement::OptimizeTable { .. } => Span::empty(),

src/dialect/postgresql.rs

+52-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
// limitations under the License.
2929
use log::debug;
3030

31-
use crate::ast::{ObjectName, Statement, UserDefinedTypeRepresentation};
31+
use crate::ast::{LockMode, ObjectName, Statement, UserDefinedTypeRepresentation};
3232
use crate::dialect::{Dialect, Precedence};
3333
use crate::keywords::Keyword;
3434
use crate::parser::{Parser, ParserError};
@@ -139,6 +139,9 @@ impl Dialect for PostgreSqlDialect {
139139
if parser.parse_keyword(Keyword::CREATE) {
140140
parser.prev_token(); // unconsume the CREATE in case we don't end up parsing anything
141141
parse_create(parser)
142+
} else if parser.parse_keyword(Keyword::LOCK) {
143+
parser.prev_token(); // unconsume the LOCK in case we don't end up parsing anything
144+
Some(parse_lock_table(parser))
142145
} else {
143146
None
144147
}
@@ -266,3 +269,51 @@ pub fn parse_create_type_as_enum(
266269
representation: UserDefinedTypeRepresentation::Enum { labels },
267270
})
268271
}
272+
273+
pub fn parse_lock_table(parser: &mut Parser) -> Result<Statement, ParserError> {
274+
parser.expect_keyword(Keyword::LOCK)?;
275+
let keyword_table = parser.parse_keyword(Keyword::TABLE);
276+
let keyword_only = parser.parse_keyword(Keyword::ONLY);
277+
let tables: Vec<ObjectName> =
278+
parser.parse_comma_separated(|parser| parser.parse_object_name(false))?;
279+
let lock_mode = parse_lock_mode(parser)?;
280+
let keyword_nowait = parser.parse_keyword(Keyword::NOWAIT);
281+
282+
Ok(Statement::LockTablesPG {
283+
keyword_table,
284+
keyword_only,
285+
tables,
286+
lock_mode,
287+
keyword_nowait,
288+
})
289+
}
290+
291+
pub fn parse_lock_mode(parser: &mut Parser) -> Result<Option<LockMode>, ParserError> {
292+
if !parser.parse_keyword(Keyword::IN) {
293+
return Ok(None);
294+
}
295+
296+
let lock_mode = if parser.parse_keywords(&[Keyword::ACCESS, Keyword::SHARE]) {
297+
LockMode::AccessShare
298+
} else if parser.parse_keywords(&[Keyword::ACCESS, Keyword::EXCLUSIVE]) {
299+
LockMode::AccessExclusive
300+
} else if parser.parse_keywords(&[Keyword::EXCLUSIVE]) {
301+
LockMode::Exclusive
302+
} else if parser.parse_keywords(&[Keyword::ROW, Keyword::EXCLUSIVE]) {
303+
LockMode::RowExclusive
304+
} else if parser.parse_keywords(&[Keyword::ROW, Keyword::SHARE]) {
305+
LockMode::RowShare
306+
} else if parser.parse_keywords(&[Keyword::SHARE, Keyword::ROW, Keyword::EXCLUSIVE]) {
307+
LockMode::ShareRowExclusive
308+
} else if parser.parse_keywords(&[Keyword::SHARE, Keyword::UPDATE, Keyword::EXCLUSIVE]) {
309+
LockMode::ShareUpdateExclusive
310+
} else if parser.parse_keywords(&[Keyword::SHARE]) {
311+
LockMode::Share
312+
} else {
313+
return Err(ParserError::ParserError("Expected: ACCESS EXCLUSIVE | ACCESS SHARE | EXCLUSIVE | ROW EXCLUSIVE | ROW SHARE | SHARE | SHARE ROW EXCLUSIVE | SHARE ROW EXCLUSIVE".into()));
314+
};
315+
316+
parser.expect_keyword(Keyword::MODE)?;
317+
318+
Ok(Some(lock_mode))
319+
}

tests/sqlparser_postgres.rs

+27
Original file line numberDiff line numberDiff line change
@@ -5200,3 +5200,30 @@ fn parse_bitstring_literal() {
52005200
fn parse_select_without_projection() {
52015201
pg_and_generic().verified_stmt("SELECT FROM users");
52025202
}
5203+
5204+
#[test]
5205+
fn parse_lock_table() {
5206+
pg().verified_stmt("LOCK customers");
5207+
pg().verified_stmt("LOCK TABLE customers");
5208+
pg().verified_stmt("LOCK TABLE ONLY customers");
5209+
pg().verified_stmt("LOCK TABLE ONLY customers IN ACCESS SHARE MODE");
5210+
pg().verified_stmt("LOCK TABLE ONLY customers IN ROW SHARE MODE");
5211+
pg().verified_stmt("LOCK TABLE ONLY customers IN ROW EXCLUSIVE MODE");
5212+
pg().verified_stmt("LOCK TABLE ONLY customers IN SHARE UPDATE EXCLUSIVE MODE");
5213+
pg().verified_stmt("LOCK TABLE ONLY customers IN SHARE MODE");
5214+
pg().verified_stmt("LOCK TABLE ONLY customers IN SHARE ROW EXCLUSIVE MODE");
5215+
pg().verified_stmt("LOCK TABLE ONLY customers IN EXCLUSIVE MODE");
5216+
pg().verified_stmt("LOCK TABLE ONLY customers IN ACCESS EXCLUSIVE MODE");
5217+
pg().verified_stmt("LOCK customers, orders");
5218+
pg().verified_stmt("LOCK TABLE customers, orders");
5219+
pg().verified_stmt("LOCK TABLE ONLY customers, orders");
5220+
pg().verified_stmt("LOCK TABLE ONLY customers, orders IN ACCESS SHARE MODE");
5221+
pg().verified_stmt("LOCK TABLE ONLY customers, orders IN ROW SHARE MODE");
5222+
pg().verified_stmt("LOCK TABLE ONLY customers, orders IN ROW EXCLUSIVE MODE");
5223+
pg().verified_stmt("LOCK TABLE ONLY customers, orders IN SHARE UPDATE EXCLUSIVE MODE");
5224+
pg().verified_stmt("LOCK TABLE ONLY customers, orders IN SHARE MODE");
5225+
pg().verified_stmt("LOCK TABLE ONLY customers, orders IN SHARE ROW EXCLUSIVE MODE");
5226+
pg().verified_stmt("LOCK TABLE ONLY customers, orders IN EXCLUSIVE MODE");
5227+
pg().verified_stmt("LOCK TABLE ONLY customers, orders IN ACCESS EXCLUSIVE MODE");
5228+
pg().verified_stmt("LOCK TABLE ONLY customers, orders IN ACCESS SHARE MODE NOWAIT");
5229+
}

0 commit comments

Comments
 (0)