Skip to content

Commit 736cbce

Browse files
committed
feat: Initial support for DECLARE (cursors)
1 parent 4070f3e commit 736cbce

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

src/ast/mod.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,29 @@ pub enum Statement {
903903
/// deleted along with the dropped table
904904
purge: bool,
905905
},
906+
/// DECLARE - Declaring Cursor Variables
907+
///
908+
/// Note: this is a PostgreSQL-specific statement,
909+
/// but may also compatible with other SQL.
910+
Declare {
911+
/// Cursor name
912+
name: ObjectName,
913+
/// Causes the cursor to return data in binary rather than in text format.
914+
binary: bool,
915+
/// None = Not specified
916+
/// Some(true) = INSENSITIVE
917+
/// Some(false) = ASENSITIVE
918+
sensitive: Option<bool>,
919+
/// None = Not specified
920+
/// Some(true) = SCROLL
921+
/// Some(false) = NO SCROLL
922+
scroll: Option<bool>,
923+
/// None = Not specified
924+
/// Some(true) = WITH HOLD, specifies that the cursor can continue to be used after the transaction that created it successfully commits
925+
/// Some(false) = WITHOUT HOLD, specifies that the cursor cannot be used outside of the transaction that created it
926+
hold: Option<bool>,
927+
query: Box<Query>,
928+
},
906929
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
907930
///
908931
/// Note: this is a PostgreSQL-specific statement,
@@ -1113,6 +1136,48 @@ impl fmt::Display for Statement {
11131136
write!(f, "{}", statement)
11141137
}
11151138
Statement::Query(s) => write!(f, "{}", s),
1139+
Statement::Declare {
1140+
name,
1141+
binary,
1142+
sensitive,
1143+
scroll,
1144+
hold,
1145+
query,
1146+
} => {
1147+
write!(f, "DECLARE {} ", name)?;
1148+
1149+
if *binary {
1150+
write!(f, "BINARY ")?;
1151+
}
1152+
1153+
if let Some(sensitive) = sensitive {
1154+
if *sensitive {
1155+
write!(f, "INSENSITIVE ")?;
1156+
} else {
1157+
write!(f, "ASENSITIVE ")?;
1158+
}
1159+
}
1160+
1161+
if let Some(scroll) = scroll {
1162+
if *scroll {
1163+
write!(f, "SCROLL ")?;
1164+
} else {
1165+
write!(f, "NO SCROLL ")?;
1166+
}
1167+
}
1168+
1169+
write!(f, "CURSOR ")?;
1170+
1171+
if let Some(hold) = hold {
1172+
if *hold {
1173+
write!(f, "WITH HOLD ")?;
1174+
} else {
1175+
write!(f, "WITHOUT HOLD ")?;
1176+
}
1177+
}
1178+
1179+
write!(f, "FOR {}", query)
1180+
}
11161181
Statement::Directory {
11171182
overwrite,
11181183
local,

src/parser.rs

Lines changed: 51 additions & 0 deletions
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::DELETE => Ok(self.parse_delete()?),
171172
Keyword::INSERT => Ok(self.parse_insert()?),
172173
Keyword::UPDATE => Ok(self.parse_update()?),
@@ -1779,6 +1780,56 @@ impl<'a> Parser<'a> {
17791780
})
17801781
}
17811782

1783+
/// DECLARE name [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ]
1784+
// CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
1785+
pub fn parse_declare(&mut self) -> Result<Statement, ParserError> {
1786+
let name = self.parse_object_name()?;
1787+
1788+
let binary = self.parse_keyword(Keyword::BINARY);
1789+
let sensitive = if self.parse_keyword(Keyword::INSENSITIVE) {
1790+
Some(true)
1791+
} else if self.parse_keyword(Keyword::ASENSITIVE) {
1792+
Some(false)
1793+
} else {
1794+
None
1795+
};
1796+
let scroll = if self.parse_keyword(Keyword::SCROLL) {
1797+
Some(true)
1798+
} else if self.parse_keywords(&[Keyword::NO, Keyword::SCROLL]) {
1799+
Some(false)
1800+
} else {
1801+
None
1802+
};
1803+
1804+
self.expect_keyword(Keyword::CURSOR)?;
1805+
1806+
let hold = match self.parse_one_of_keywords(&[Keyword::WITH, Keyword::WITHOUT]) {
1807+
Some(keyword) => {
1808+
self.expect_keyword(Keyword::HOLD)?;
1809+
1810+
match keyword {
1811+
Keyword::WITH => Some(true),
1812+
Keyword::WITHOUT => Some(false),
1813+
_ => unreachable!(),
1814+
}
1815+
}
1816+
None => None,
1817+
};
1818+
1819+
self.expect_keyword(Keyword::FOR)?;
1820+
1821+
let query = self.parse_query()?;
1822+
1823+
Ok(Statement::Declare {
1824+
name,
1825+
binary,
1826+
sensitive,
1827+
scroll,
1828+
hold,
1829+
query: Box::new(query),
1830+
})
1831+
}
1832+
17821833
pub fn parse_discard(&mut self) -> Result<Statement, ParserError> {
17831834
let object_type = if self.parse_keyword(Keyword::ALL) {
17841835
DiscardObject::ALL

tests/sqlparser_postgres.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,3 +1467,20 @@ fn pg_and_generic() -> TestedDialects {
14671467
dialects: vec![Box::new(PostgreSqlDialect {}), Box::new(GenericDialect {})],
14681468
}
14691469
}
1470+
1471+
#[test]
1472+
fn parse_declare() {
1473+
pg_and_generic()
1474+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" CURSOR WITH HOLD FOR SELECT 1");
1475+
pg_and_generic()
1476+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" CURSOR WITHOUT HOLD FOR SELECT 1");
1477+
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY CURSOR FOR SELECT 1");
1478+
pg_and_generic()
1479+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" ASENSITIVE CURSOR FOR SELECT 1");
1480+
pg_and_generic()
1481+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" INSENSITIVE CURSOR FOR SELECT 1");
1482+
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" SCROLL CURSOR FOR SELECT 1");
1483+
pg_and_generic()
1484+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" NO SCROLL CURSOR FOR SELECT 1");
1485+
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY INSENSITIVE SCROLL CURSOR WITH HOLD FOR SELECT * FROM table_name LIMIT 2222");
1486+
}

0 commit comments

Comments
 (0)