feat: Initial support for DECLARE (cursors) (#509)

This commit is contained in:
Dmitry Patsura 2022-06-06 14:22:12 +03:00 committed by GitHub
parent 66a3082cb6
commit 3f1c6426f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 133 additions and 0 deletions

View file

@ -895,6 +895,29 @@ pub enum Statement {
/// deleted along with the dropped table
purge: bool,
},
/// DECLARE - Declaring Cursor Variables
///
/// Note: this is a PostgreSQL-specific statement,
/// but may also compatible with other SQL.
Declare {
/// Cursor name
name: Ident,
/// Causes the cursor to return data in binary rather than in text format.
binary: bool,
/// None = Not specified
/// Some(true) = INSENSITIVE
/// Some(false) = ASENSITIVE
sensitive: Option<bool>,
/// None = Not specified
/// Some(true) = SCROLL
/// Some(false) = NO SCROLL
scroll: Option<bool>,
/// None = Not specified
/// Some(true) = WITH HOLD, specifies that the cursor can continue to be used after the transaction that created it successfully commits
/// Some(false) = WITHOUT HOLD, specifies that the cursor cannot be used outside of the transaction that created it
hold: Option<bool>,
query: Box<Query>,
},
/// FETCH - retrieve rows from a query using a cursor
///
/// Note: this is a PostgreSQL-specific statement,
@ -1125,6 +1148,48 @@ impl fmt::Display for Statement {
write!(f, "{}", statement)
}
Statement::Query(s) => write!(f, "{}", s),
Statement::Declare {
name,
binary,
sensitive,
scroll,
hold,
query,
} => {
write!(f, "DECLARE {} ", name)?;
if *binary {
write!(f, "BINARY ")?;
}
if let Some(sensitive) = sensitive {
if *sensitive {
write!(f, "INSENSITIVE ")?;
} else {
write!(f, "ASENSITIVE ")?;
}
}
if let Some(scroll) = scroll {
if *scroll {
write!(f, "SCROLL ")?;
} else {
write!(f, "NO SCROLL ")?;
}
}
write!(f, "CURSOR ")?;
if let Some(hold) = hold {
if *hold {
write!(f, "WITH HOLD ")?;
} else {
write!(f, "WITHOUT HOLD ")?;
}
}
write!(f, "FOR {}", query)
}
Statement::Fetch {
name,
direction,

View file

@ -167,6 +167,7 @@ impl<'a> Parser<'a> {
Keyword::CREATE => Ok(self.parse_create()?),
Keyword::DROP => Ok(self.parse_drop()?),
Keyword::DISCARD => Ok(self.parse_discard()?),
Keyword::DECLARE => Ok(self.parse_declare()?),
Keyword::FETCH => Ok(self.parse_fetch_statement()?),
Keyword::DELETE => Ok(self.parse_delete()?),
Keyword::INSERT => Ok(self.parse_insert()?),
@ -1825,6 +1826,56 @@ impl<'a> Parser<'a> {
})
}
/// DECLARE name [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ]
// CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
pub fn parse_declare(&mut self) -> Result<Statement, ParserError> {
let name = self.parse_identifier()?;
let binary = self.parse_keyword(Keyword::BINARY);
let sensitive = if self.parse_keyword(Keyword::INSENSITIVE) {
Some(true)
} else if self.parse_keyword(Keyword::ASENSITIVE) {
Some(false)
} else {
None
};
let scroll = if self.parse_keyword(Keyword::SCROLL) {
Some(true)
} else if self.parse_keywords(&[Keyword::NO, Keyword::SCROLL]) {
Some(false)
} else {
None
};
self.expect_keyword(Keyword::CURSOR)?;
let hold = match self.parse_one_of_keywords(&[Keyword::WITH, Keyword::WITHOUT]) {
Some(keyword) => {
self.expect_keyword(Keyword::HOLD)?;
match keyword {
Keyword::WITH => Some(true),
Keyword::WITHOUT => Some(false),
_ => unreachable!(),
}
}
None => None,
};
self.expect_keyword(Keyword::FOR)?;
let query = self.parse_query()?;
Ok(Statement::Declare {
name,
binary,
sensitive,
scroll,
hold,
query: Box::new(query),
})
}
// FETCH [ direction { FROM | IN } ] cursor INTO target;
pub fn parse_fetch_statement(&mut self) -> Result<Statement, ParserError> {
let direction = if self.parse_keyword(Keyword::NEXT) {

View file

@ -1514,6 +1514,23 @@ fn parse_escaped_literal_string() {
);
}
#[test]
fn parse_declare() {
pg_and_generic()
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" CURSOR WITH HOLD FOR SELECT 1");
pg_and_generic()
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" CURSOR WITHOUT HOLD FOR SELECT 1");
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY CURSOR FOR SELECT 1");
pg_and_generic()
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" ASENSITIVE CURSOR FOR SELECT 1");
pg_and_generic()
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" INSENSITIVE CURSOR FOR SELECT 1");
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" SCROLL CURSOR FOR SELECT 1");
pg_and_generic()
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" NO SCROLL CURSOR FOR SELECT 1");
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY INSENSITIVE SCROLL CURSOR WITH HOLD FOR SELECT * FROM table_name LIMIT 2222");
}
#[test]
fn parse_fetch() {
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\"");