Skip to content

Commit 789716f

Browse files
committed
feat: Initial support for DECLARE (cursors)
1 parent 92ac0de commit 789716f

3 files changed

Lines changed: 133 additions & 0 deletions

File tree

src/ast/mod.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,29 @@ pub enum Statement {
858858
/// deleted along with the dropped table
859859
purge: bool,
860860
},
861+
/// DECLARE - Declaring Cursor Variables
862+
///
863+
/// Note: this is a PostgreSQL-specific statement,
864+
/// but may also compatible with other SQL.
865+
Declare {
866+
/// Cursor name
867+
name: Ident,
868+
/// Causes the cursor to return data in binary rather than in text format.
869+
binary: bool,
870+
/// None = Not specified
871+
/// Some(true) = INSENSITIVE
872+
/// Some(false) = ASENSITIVE
873+
sensitive: Option<bool>,
874+
/// None = Not specified
875+
/// Some(true) = SCROLL
876+
/// Some(false) = NO SCROLL
877+
scroll: Option<bool>,
878+
/// None = Not specified
879+
/// Some(true) = WITH HOLD, specifies that the cursor can continue to be used after the transaction that created it successfully commits
880+
/// Some(false) = WITHOUT HOLD, specifies that the cursor cannot be used outside of the transaction that created it
881+
hold: Option<bool>,
882+
query: Box<Query>,
883+
},
861884
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
862885
///
863886
/// Note: this is a PostgreSQL-specific statement,
@@ -1082,6 +1105,48 @@ impl fmt::Display for Statement {
10821105
write!(f, "{}", statement)
10831106
}
10841107
Statement::Query(s) => write!(f, "{}", s),
1108+
Statement::Declare {
1109+
name,
1110+
binary,
1111+
sensitive,
1112+
scroll,
1113+
hold,
1114+
query,
1115+
} => {
1116+
write!(f, "DECLARE {} ", name)?;
1117+
1118+
if *binary {
1119+
write!(f, "BINARY ")?;
1120+
}
1121+
1122+
if let Some(sensitive) = sensitive {
1123+
if *sensitive {
1124+
write!(f, "INSENSITIVE ")?;
1125+
} else {
1126+
write!(f, "ASENSITIVE ")?;
1127+
}
1128+
}
1129+
1130+
if let Some(scroll) = scroll {
1131+
if *scroll {
1132+
write!(f, "SCROLL ")?;
1133+
} else {
1134+
write!(f, "NO SCROLL ")?;
1135+
}
1136+
}
1137+
1138+
write!(f, "CURSOR ")?;
1139+
1140+
if let Some(hold) = hold {
1141+
if *hold {
1142+
write!(f, "WITH HOLD ")?;
1143+
} else {
1144+
write!(f, "WITHOUT HOLD ")?;
1145+
}
1146+
}
1147+
1148+
write!(f, "FOR {}", query)
1149+
}
10851150
Statement::Directory {
10861151
overwrite,
10871152
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()?),
@@ -1753,6 +1754,56 @@ impl<'a> Parser<'a> {
17531754
})
17541755
}
17551756

1757+
/// DECLARE name [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ]
1758+
// CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
1759+
pub fn parse_declare(&mut self) -> Result<Statement, ParserError> {
1760+
let name = self.parse_identifier()?;
1761+
1762+
let binary = self.parse_keyword(Keyword::BINARY);
1763+
let sensitive = if self.parse_keyword(Keyword::INSENSITIVE) {
1764+
Some(true)
1765+
} else if self.parse_keyword(Keyword::ASENSITIVE) {
1766+
Some(false)
1767+
} else {
1768+
None
1769+
};
1770+
let scroll = if self.parse_keyword(Keyword::SCROLL) {
1771+
Some(true)
1772+
} else if self.parse_keywords(&[Keyword::NO, Keyword::SCROLL]) {
1773+
Some(false)
1774+
} else {
1775+
None
1776+
};
1777+
1778+
self.expect_keyword(Keyword::CURSOR)?;
1779+
1780+
let hold = match self.parse_one_of_keywords(&[Keyword::WITH, Keyword::WITHOUT]) {
1781+
Some(keyword) => {
1782+
self.expect_keyword(Keyword::HOLD)?;
1783+
1784+
match keyword {
1785+
Keyword::WITH => Some(true),
1786+
Keyword::WITHOUT => Some(false),
1787+
_ => unreachable!(),
1788+
}
1789+
}
1790+
None => None,
1791+
};
1792+
1793+
self.expect_keyword(Keyword::FOR)?;
1794+
1795+
let query = self.parse_query()?;
1796+
1797+
Ok(Statement::Declare {
1798+
name,
1799+
binary,
1800+
sensitive,
1801+
scroll,
1802+
hold,
1803+
query: Box::new(query),
1804+
})
1805+
}
1806+
17561807
pub fn parse_discard(&mut self) -> Result<Statement, ParserError> {
17571808
let object_type = if self.parse_keyword(Keyword::ALL) {
17581809
DiscardObject::ALL

tests/sqlparser_postgres.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,3 +1334,20 @@ fn parse_escaped_literal_string() {
13341334
"sql parser error: Unterminated encoded string literal at Line: 1, Column 8"
13351335
);
13361336
}
1337+
1338+
#[test]
1339+
fn parse_declare() {
1340+
pg_and_generic()
1341+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" CURSOR WITH HOLD FOR SELECT 1");
1342+
pg_and_generic()
1343+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" CURSOR WITHOUT HOLD FOR SELECT 1");
1344+
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY CURSOR FOR SELECT 1");
1345+
pg_and_generic()
1346+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" ASENSITIVE CURSOR FOR SELECT 1");
1347+
pg_and_generic()
1348+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" INSENSITIVE CURSOR FOR SELECT 1");
1349+
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" SCROLL CURSOR FOR SELECT 1");
1350+
pg_and_generic()
1351+
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" NO SCROLL CURSOR FOR SELECT 1");
1352+
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY INSENSITIVE SCROLL CURSOR WITH HOLD FOR SELECT * FROM table_name LIMIT 2222");
1353+
}

0 commit comments

Comments
 (0)