Skip to content

Commit 86cd1fc

Browse files
committed
feat: Initial support for DECLARE (cursors)
1 parent aa46e93 commit 86cd1fc

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
@@ -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
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
899922
///
900923
/// Note: this is a PostgreSQL-specific statement,
@@ -1114,6 +1137,48 @@ impl fmt::Display for Statement {
11141137
write!(f, "{}", statement)
11151138
}
11161139
Statement::Query(s) => write!(f, "{}", s),
1140+
Statement::Declare {
1141+
name,
1142+
binary,
1143+
sensitive,
1144+
scroll,
1145+
hold,
1146+
query,
1147+
} => {
1148+
write!(f, "DECLARE {} ", name)?;
1149+
1150+
if *binary {
1151+
write!(f, "BINARY ")?;
1152+
}
1153+
1154+
if let Some(sensitive) = sensitive {
1155+
if *sensitive {
1156+
write!(f, "INSENSITIVE ")?;
1157+
} else {
1158+
write!(f, "ASENSITIVE ")?;
1159+
}
1160+
}
1161+
1162+
if let Some(scroll) = scroll {
1163+
if *scroll {
1164+
write!(f, "SCROLL ")?;
1165+
} else {
1166+
write!(f, "NO SCROLL ")?;
1167+
}
1168+
}
1169+
1170+
write!(f, "CURSOR ")?;
1171+
1172+
if let Some(hold) = hold {
1173+
if *hold {
1174+
write!(f, "WITH HOLD ")?;
1175+
} else {
1176+
write!(f, "WITHOUT HOLD ")?;
1177+
}
1178+
}
1179+
1180+
write!(f, "FOR {}", query)
1181+
}
11171182
Statement::Directory {
11181183
overwrite,
11191184
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()?),
@@ -1824,6 +1825,56 @@ impl<'a> Parser<'a> {
18241825
})
18251826
}
18261827

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

tests/sqlparser_postgres.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,3 +1513,20 @@ fn parse_escaped_literal_string() {
15131513
"sql parser error: Unterminated encoded string literal at Line: 1, Column 8"
15141514
);
15151515
}
1516+
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+
}

0 commit comments

Comments
 (0)