feat: add FOR UPDATE/FOR SHARE clause (#418)

* feat: add FOR UPDATE/FOR SHARE clause

* refactor: LockType enum variant name

Co-authored-by: gamife <gamife9886@gmail.com>
This commit is contained in:
gamife 2022-02-24 13:39:38 +08:00 committed by GitHub
parent 899f91b1f6
commit 0b5178d7e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 59 additions and 4 deletions

View file

@ -35,9 +35,9 @@ pub use self::ddl::{
}; };
pub use self::operator::{BinaryOperator, UnaryOperator}; pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{ pub use self::query::{
Cte, Fetch, Join, JoinConstraint, JoinOperator, LateralView, Offset, OffsetRows, OrderByExpr, Cte, Fetch, Join, JoinConstraint, JoinOperator, LateralView, LockType, Offset, OffsetRows,
Query, Select, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Top, OrderByExpr, Query, Select, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor,
Values, With, TableWithJoins, Top, Values, With,
}; };
pub use self::value::{DateTimeField, TrimWhereField, Value}; pub use self::value::{DateTimeField, TrimWhereField, Value};

View file

@ -35,6 +35,8 @@ pub struct Query {
pub offset: Option<Offset>, pub offset: Option<Offset>,
/// `FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }` /// `FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }`
pub fetch: Option<Fetch>, pub fetch: Option<Fetch>,
/// `FOR { UPDATE | SHARE }`
pub lock: Option<LockType>,
} }
impl fmt::Display for Query { impl fmt::Display for Query {
@ -55,6 +57,9 @@ impl fmt::Display for Query {
if let Some(ref fetch) = self.fetch { if let Some(ref fetch) = self.fetch {
write!(f, " {}", fetch)?; write!(f, " {}", fetch)?;
} }
if let Some(ref lock) = self.lock {
write!(f, " {}", lock)?;
}
Ok(()) Ok(())
} }
} }
@ -577,6 +582,23 @@ impl fmt::Display for Fetch {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum LockType {
Share,
Update,
}
impl fmt::Display for LockType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let select_lock = match self {
LockType::Share => "FOR SHARE",
LockType::Update => "FOR UPDATE",
};
write!(f, "{}", select_lock)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Top { pub struct Top {

View file

@ -415,6 +415,7 @@ define_keywords!(
SESSION_USER, SESSION_USER,
SET, SET,
SETS, SETS,
SHARE,
SHOW, SHOW,
SIMILAR, SIMILAR,
SMALLINT, SMALLINT,

View file

@ -2649,6 +2649,12 @@ impl<'a> Parser<'a> {
None None
}; };
let lock = if self.parse_keyword(Keyword::FOR) {
Some(self.parse_lock()?)
} else {
None
};
Ok(Query { Ok(Query {
with, with,
body, body,
@ -2656,6 +2662,7 @@ impl<'a> Parser<'a> {
limit, limit,
offset, offset,
fetch, fetch,
lock,
}) })
} else { } else {
let insert = self.parse_insert()?; let insert = self.parse_insert()?;
@ -2667,6 +2674,7 @@ impl<'a> Parser<'a> {
order_by: vec![], order_by: vec![],
offset: None, offset: None,
fetch: None, fetch: None,
lock: None,
}) })
} }
} }
@ -3639,6 +3647,15 @@ impl<'a> Parser<'a> {
}) })
} }
/// Parse a FOR UPDATE/FOR SHARE clause
pub fn parse_lock(&mut self) -> Result<LockType, ParserError> {
match self.expect_one_of_keywords(&[Keyword::UPDATE, Keyword::SHARE])? {
Keyword::UPDATE => Ok(LockType::Update),
Keyword::SHARE => Ok(LockType::Share),
_ => unreachable!(),
}
}
pub fn parse_values(&mut self) -> Result<Values, ParserError> { pub fn parse_values(&mut self) -> Result<Values, ParserError> {
let values = self.parse_comma_separated(|parser| { let values = self.parse_comma_separated(|parser| {
parser.expect_token(&Token::LParen)?; parser.expect_token(&Token::LParen)?;

View file

@ -4162,6 +4162,17 @@ fn test_revoke() {
} }
} }
#[test]
fn test_lock() {
let sql = "SELECT * FROM student WHERE id = '1' FOR UPDATE";
let ast = verified_query(sql);
assert_eq!(ast.lock.unwrap(), LockType::Update);
let sql = "SELECT * FROM student WHERE id = '1' FOR SHARE";
let ast = verified_query(sql);
assert_eq!(ast.lock.unwrap(), LockType::Share);
}
#[test] #[test]
fn test_placeholder() { fn test_placeholder() {
let sql = "SELECT * FROM student WHERE id = ?"; let sql = "SELECT * FROM student WHERE id = ?";

View file

@ -292,6 +292,7 @@ fn parse_quote_identifiers_2() {
limit: None, limit: None,
offset: None, offset: None,
fetch: None, fetch: None,
lock: None,
})) }))
); );
} }
@ -417,6 +418,7 @@ fn parse_simple_insert() {
limit: None, limit: None,
offset: None, offset: None,
fetch: None, fetch: None,
lock: None,
}), }),
source source
); );
@ -469,6 +471,7 @@ fn parse_insert_with_on_duplicate_update() {
limit: None, limit: None,
offset: None, offset: None,
fetch: None, fetch: None,
lock: None,
}), }),
source source
); );
@ -686,7 +689,8 @@ fn parse_substring_in_select() {
order_by: vec![], order_by: vec![],
limit: None, limit: None,
offset: None, offset: None,
fetch: None fetch: None,
lock: None,
}), }),
query query
); );