Skip to content

Commit 3f1c642

Browse files
authored
feat: Initial support for DECLARE (cursors) (#509)
1 parent 66a3082 commit 3f1c642

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

src/ast/mod.rs

+65
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,29 @@ pub enum Statement {
895895
/// deleted along with the dropped table
896896
purge: bool,
897897
},
898+
/// DECLARE - Declaring Cursor Variables
899+
///
900+
/// Note: this is a PostgreSQL-specific statement,
901+
/// but may also compatible with other SQL.
902+
Declare {
903+
/// Cursor name
904+
name: Ident,
905+
/// Causes the cursor to return data in binary rather than in text format.
906+
binary: bool,
907+
/// None = Not specified
908+
/// Some(true) = INSENSITIVE
909+
/// Some(false) = ASENSITIVE
910+
sensitive: Option<bool>,
911+
/// None = Not specified
912+
/// Some(true) = SCROLL
913+
/// Some(false) = NO SCROLL
914+
scroll: Option<bool>,
915+
/// None = Not specified
916+
/// Some(true) = WITH HOLD, specifies that the cursor can continue to be used after the transaction that created it successfully commits
917+
/// Some(false) = WITHOUT HOLD, specifies that the cursor cannot be used outside of the transaction that created it
918+
hold: Option<bool>,
919+
query: Box<Query>,
920+
},
898921
/// FETCH - retrieve rows from a query using a cursor
899922
///
900923
/// Note: this is a PostgreSQL-specific statement,
@@ -1125,6 +1148,48 @@ impl fmt::Display for Statement {
11251148
write!(f, "{}", statement)
11261149
}
11271150
Statement::Query(s) => write!(f, "{}", s),
1151+
Statement::Declare {
1152+
name,
1153+
binary,
1154+
sensitive,
1155+
scroll,
1156+
hold,
1157+
query,
1158+
} => {
1159+
write!(f, "DECLARE {} ", name)?;
1160+
1161+
if *binary {
1162+
write!(f, "BINARY ")?;
1163+
}
1164+
1165+
if let Some(sensitive) = sensitive {
1166+
if *sensitive {
1167+
write!(f, "INSENSITIVE ")?;
1168+
} else {
1169+
write!(f, "ASENSITIVE ")?;
1170+
}
1171+
}
1172+
1173+
if let Some(scroll) = scroll {
1174+
if *scroll {
1175+
write!(f, "SCROLL ")?;
1176+
} else {
1177+
write!(f, "NO SCROLL ")?;
1178+
}
1179+
}
1180+
1181+
write!(f, "CURSOR ")?;
1182+
1183+
if let Some(hold) = hold {
1184+
if *hold {
1185+
write!(f, "WITH HOLD ")?;
1186+
} else {
1187+
write!(f, "WITHOUT HOLD ")?;
1188+
}
1189+
}
1190+
1191+
write!(f, "FOR {}", query)
1192+
}
11281193
Statement::Fetch {
11291194
name,
11301195
direction,

src/parser.rs

+51
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ impl<'a> Parser<'a> {
167167
Keyword::CREATE => Ok(self.parse_create()?),
168168
Keyword::DROP => Ok(self.parse_drop()?),
169169
Keyword::DISCARD => Ok(self.parse_discard()?),
170+
Keyword::DECLARE => Ok(self.parse_declare()?),
170171
Keyword::FETCH => Ok(self.parse_fetch_statement()?),
171172
Keyword::DELETE => Ok(self.parse_delete()?),
172173
Keyword::INSERT => Ok(self.parse_insert()?),
@@ -1825,6 +1826,56 @@ impl<'a> Parser<'a> {
18251826
})
18261827
}
18271828

1829+
/// DECLARE name [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ]
1830+
// CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
1831+
pub fn parse_declare(&mut self) -> Result<Statement, ParserError> {
1832+
let name = self.parse_identifier()?;
1833+
1834+
let binary = self.parse_keyword(Keyword::BINARY);
1835+
let sensitive = if self.parse_keyword(Keyword::INSENSITIVE) {
1836+
Some(true)
1837+
} else if self.parse_keyword(Keyword::ASENSITIVE) {
1838+
Some(false)
1839+
} else {
1840+
None
1841+
};
1842+
let scroll = if self.parse_keyword(Keyword::SCROLL) {
1843+
Some(true)
1844+
} else if self.parse_keywords(&[Keyword::NO, Keyword::SCROLL]) {
1845+
Some(false)
1846+
} else {
1847+
None
1848+
};
1849+
1850+
self.expect_keyword(Keyword::CURSOR)?;
1851+
1852+
let hold = match self.parse_one_of_keywords(&[Keyword::WITH, Keyword::WITHOUT]) {
1853+
Some(keyword) => {
1854+
self.expect_keyword(Keyword::HOLD)?;
1855+
1856+
match keyword {
1857+
Keyword::WITH => Some(true),
1858+
Keyword::WITHOUT => Some(false),
1859+
_ => unreachable!(),
1860+
}
1861+
}
1862+
None => None,
1863+
};
1864+
1865+
self.expect_keyword(Keyword::FOR)?;
1866+
1867+
let query = self.parse_query()?;
1868+
1869+
Ok(Statement::Declare {
1870+
name,
1871+
binary,
1872+
sensitive,
1873+
scroll,
1874+
hold,
1875+
query: Box::new(query),
1876+
})
1877+
}
1878+
18281879
// FETCH [ direction { FROM | IN } ] cursor INTO target;
18291880
pub fn parse_fetch_statement(&mut self) -> Result<Statement, ParserError> {
18301881
let direction = if self.parse_keyword(Keyword::NEXT) {

tests/sqlparser_postgres.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,23 @@ fn parse_escaped_literal_string() {
15141514
);
15151515
}
15161516

1517+
#[test]
1518+
fn parse_declare() {
1519+
pg_and_generic()
1520+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" CURSOR WITH HOLD FOR SELECT 1");
1521+
pg_and_generic()
1522+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" CURSOR WITHOUT HOLD FOR SELECT 1");
1523+
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY CURSOR FOR SELECT 1");
1524+
pg_and_generic()
1525+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" ASENSITIVE CURSOR FOR SELECT 1");
1526+
pg_and_generic()
1527+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" INSENSITIVE CURSOR FOR SELECT 1");
1528+
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" SCROLL CURSOR FOR SELECT 1");
1529+
pg_and_generic()
1530+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" NO SCROLL CURSOR FOR SELECT 1");
1531+
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY INSENSITIVE SCROLL CURSOR WITH HOLD FOR SELECT * FROM table_name LIMIT 2222");
1532+
}
1533+
15171534
#[test]
15181535
fn parse_fetch() {
15191536
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\"");

0 commit comments

Comments
 (0)