Skip to content

Commit 2fdd894

Browse files
committed
feat: Support FETCH (cursors)
1 parent 789716f commit 2fdd894

4 files changed

Lines changed: 182 additions & 1 deletion

File tree

src/ast/mod.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,18 @@ pub enum Statement {
859859
purge: bool,
860860
},
861861
/// DECLARE - Declaring Cursor Variables
862+
/// FETCH - retrieve rows from a query using a cursor
863+
///
864+
/// Note: this is a PostgreSQL-specific statement,
865+
/// but may also compatible with other SQL.
866+
Fetch {
867+
/// Cursor name
868+
name: Ident,
869+
direction: FetchDirection,
870+
/// Optional, It's possible to fetch rows form cursor to the table
871+
into: Option<ObjectName>,
872+
},
873+
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
862874
///
863875
/// Note: this is a PostgreSQL-specific statement,
864876
/// but may also compatible with other SQL.
@@ -1147,6 +1159,21 @@ impl fmt::Display for Statement {
11471159

11481160
write!(f, "FOR {}", query)
11491161
}
1162+
Statement::Fetch {
1163+
name,
1164+
direction,
1165+
into,
1166+
} => {
1167+
write!(f, "FETCH {} ", direction)?;
1168+
1169+
write!(f, "IN {}", name)?;
1170+
1171+
if let Some(into) = into {
1172+
write!(f, " INTO {}", into)?;
1173+
}
1174+
1175+
Ok(())
1176+
}
11501177
Statement::Directory {
11511178
overwrite,
11521179
local,
@@ -1940,6 +1967,69 @@ impl fmt::Display for Privileges {
19401967
}
19411968
}
19421969

1970+
/// Specific direction for FETCH statement
1971+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1972+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1973+
pub enum FetchDirection {
1974+
Count { limit: Value },
1975+
Next,
1976+
Prior,
1977+
First,
1978+
Last,
1979+
Absolute { limit: Value },
1980+
Relative { limit: Value },
1981+
All,
1982+
// FORWARD
1983+
// FORWARD count
1984+
Forward { limit: Option<Value> },
1985+
ForwardAll,
1986+
// BACKWARD
1987+
// BACKWARD count
1988+
Backward { limit: Option<Value> },
1989+
BackwardAll,
1990+
}
1991+
1992+
impl fmt::Display for FetchDirection {
1993+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1994+
match self {
1995+
FetchDirection::Count { limit } => f.write_str(&limit.to_string())?,
1996+
FetchDirection::Next => f.write_str("NEXT")?,
1997+
FetchDirection::Prior => f.write_str("PRIOR")?,
1998+
FetchDirection::First => f.write_str("FIRST")?,
1999+
FetchDirection::Last => f.write_str("LAST")?,
2000+
FetchDirection::Absolute { limit } => {
2001+
f.write_str("ABSOLUTE ")?;
2002+
f.write_str(&limit.to_string())?;
2003+
}
2004+
FetchDirection::Relative { limit } => {
2005+
f.write_str("RELATIVE ")?;
2006+
f.write_str(&limit.to_string())?;
2007+
}
2008+
FetchDirection::All => f.write_str("ALL")?,
2009+
FetchDirection::Forward { limit } => {
2010+
f.write_str("FORWARD")?;
2011+
2012+
if let Some(l) = limit {
2013+
f.write_str(" ")?;
2014+
f.write_str(&l.to_string())?;
2015+
}
2016+
}
2017+
FetchDirection::ForwardAll => f.write_str("FORWARD ALL")?,
2018+
FetchDirection::Backward { limit } => {
2019+
f.write_str("BACKWARD")?;
2020+
2021+
if let Some(l) = limit {
2022+
f.write_str(" ")?;
2023+
f.write_str(&l.to_string())?;
2024+
}
2025+
}
2026+
FetchDirection::BackwardAll => f.write_str("BACKWARD ALL")?,
2027+
};
2028+
2029+
Ok(())
2030+
}
2031+
}
2032+
19432033
/// A privilege on a database object (table, sequence, etc.).
19442034
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19452035
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

src/keywords.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ macro_rules! define_keywords {
6767
define_keywords!(
6868
ABORT,
6969
ABS,
70+
ABSOLUTE,
7071
ACTION,
7172
ADD,
7273
ALL,
@@ -92,6 +93,7 @@ define_keywords!(
9293
AUTO_INCREMENT,
9394
AVG,
9495
AVRO,
96+
BACKWARD,
9597
BEGIN,
9698
BEGIN_FRAME,
9799
BEGIN_PARTITION,
@@ -238,6 +240,7 @@ define_keywords!(
238240
FORCE_QUOTE,
239241
FOREIGN,
240242
FORMAT,
243+
FORWARD,
241244
FRAME_ROW,
242245
FREE,
243246
FREEZE,
@@ -384,6 +387,7 @@ define_keywords!(
384387
PREPARE,
385388
PRESERVE,
386389
PRIMARY,
390+
PRIOR,
387391
PRIVILEGES,
388392
PROCEDURE,
389393
PROGRAM,
@@ -411,6 +415,7 @@ define_keywords!(
411415
REGR_SXX,
412416
REGR_SXY,
413417
REGR_SYY,
418+
RELATIVE,
414419
RELEASE,
415420
RENAME,
416421
REPAIR,

src/parser.rs

Lines changed: 62 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::FETCH => Ok(self.parse_fetch_statement()?),
170171
Keyword::DECLARE => Ok(self.parse_declare()?),
171172
Keyword::DELETE => Ok(self.parse_delete()?),
172173
Keyword::INSERT => Ok(self.parse_insert()?),
@@ -1754,6 +1755,67 @@ impl<'a> Parser<'a> {
17541755
})
17551756
}
17561757

1758+
// FETCH [ direction { FROM | IN } ] cursor INTO target;
1759+
pub fn parse_fetch_statement(&mut self) -> Result<Statement, ParserError> {
1760+
let direction = if self.parse_keyword(Keyword::NEXT) {
1761+
FetchDirection::Next
1762+
} else if self.parse_keyword(Keyword::PRIOR) {
1763+
FetchDirection::Prior
1764+
} else if self.parse_keyword(Keyword::FIRST) {
1765+
FetchDirection::First
1766+
} else if self.parse_keyword(Keyword::LAST) {
1767+
FetchDirection::Last
1768+
} else if self.parse_keyword(Keyword::ABSOLUTE) {
1769+
FetchDirection::Absolute {
1770+
limit: self.parse_number_value()?,
1771+
}
1772+
} else if self.parse_keyword(Keyword::RELATIVE) {
1773+
FetchDirection::Relative {
1774+
limit: self.parse_number_value()?,
1775+
}
1776+
} else if self.parse_keyword(Keyword::FORWARD) {
1777+
if self.parse_keyword(Keyword::ALL) {
1778+
FetchDirection::ForwardAll
1779+
} else {
1780+
FetchDirection::Forward {
1781+
// TODO: Support optional
1782+
limit: Some(self.parse_number_value()?),
1783+
}
1784+
}
1785+
} else if self.parse_keyword(Keyword::BACKWARD) {
1786+
if self.parse_keyword(Keyword::ALL) {
1787+
FetchDirection::BackwardAll
1788+
} else {
1789+
FetchDirection::Backward {
1790+
// TODO: Support optional
1791+
limit: Some(self.parse_number_value()?),
1792+
}
1793+
}
1794+
} else if self.parse_keyword(Keyword::ALL) {
1795+
FetchDirection::All
1796+
} else {
1797+
FetchDirection::Count {
1798+
limit: self.parse_number_value()?,
1799+
}
1800+
};
1801+
1802+
self.expect_one_of_keywords(&[Keyword::FROM, Keyword::IN])?;
1803+
1804+
let name = self.parse_identifier()?;
1805+
1806+
let into = if self.parse_keyword(Keyword::INTO) {
1807+
Some(self.parse_object_name()?)
1808+
} else {
1809+
None
1810+
};
1811+
1812+
Ok(Statement::Fetch {
1813+
name,
1814+
direction,
1815+
into,
1816+
})
1817+
}
1818+
17571819
/// DECLARE name [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ]
17581820
// CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
17591821
pub fn parse_declare(&mut self) -> Result<Statement, ParserError> {

tests/sqlparser_postgres.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ fn test_copy_to() {
589589

590590
#[test]
591591
fn parse_copy_from() {
592-
let sql = "COPY table (a, b) FROM 'file.csv' WITH
592+
let sql = "COPY table (a, b) FROM 'file.csv' WITH
593593
(
594594
FORMAT CSV,
595595
FREEZE,
@@ -1335,6 +1335,30 @@ fn parse_escaped_literal_string() {
13351335
);
13361336
}
13371337

1338+
#[test]
1339+
fn parse_fetch() {
1340+
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\"");
1341+
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1342+
pg_and_generic().verified_stmt("FETCH NEXT IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1343+
pg_and_generic().verified_stmt("FETCH PRIOR IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1344+
pg_and_generic().verified_stmt("FETCH FIRST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1345+
pg_and_generic().verified_stmt("FETCH LAST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1346+
pg_and_generic()
1347+
.verified_stmt("FETCH ABSOLUTE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1348+
pg_and_generic()
1349+
.verified_stmt("FETCH RELATIVE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1350+
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1351+
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1352+
pg_and_generic()
1353+
.verified_stmt("FETCH FORWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1354+
pg_and_generic()
1355+
.verified_stmt("FETCH FORWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1356+
pg_and_generic()
1357+
.verified_stmt("FETCH BACKWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1358+
pg_and_generic()
1359+
.verified_stmt("FETCH BACKWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1360+
}
1361+
13381362
#[test]
13391363
fn parse_declare() {
13401364
pg_and_generic()

0 commit comments

Comments
 (0)