Skip to content

Commit 8004f15

Browse files
committed
feat: Support FETCH (cursors)
1 parent 736cbce commit 8004f15

File tree

4 files changed

+178
-0
lines changed

4 files changed

+178
-0
lines changed

src/ast/mod.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,17 @@ pub enum Statement {
926926
hold: Option<bool>,
927927
query: Box<Query>,
928928
},
929+
/// FETCH - retrieve rows from a query using a cursor
930+
///
931+
/// Note: this is a PostgreSQL-specific statement,
932+
/// but may also compatible with other SQL.
933+
Fetch {
934+
/// Cursor name
935+
name: ObjectName,
936+
direction: FetchDirection,
937+
/// Optional, It's possible to fetch rows form cursor to the table
938+
into: Option<ObjectName>,
939+
},
929940
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
930941
///
931942
/// Note: this is a PostgreSQL-specific statement,
@@ -1136,6 +1147,21 @@ impl fmt::Display for Statement {
11361147
write!(f, "{}", statement)
11371148
}
11381149
Statement::Query(s) => write!(f, "{}", s),
1150+
Statement::Fetch {
1151+
name,
1152+
direction,
1153+
into,
1154+
} => {
1155+
write!(f, "FETCH {} ", direction)?;
1156+
1157+
write!(f, "IN {}", name)?;
1158+
1159+
if let Some(into) = into {
1160+
write!(f, " INTO {}", into)?;
1161+
}
1162+
1163+
Ok(())
1164+
}
11391165
Statement::Declare {
11401166
name,
11411167
binary,
@@ -1907,6 +1933,67 @@ impl fmt::Display for Privileges {
19071933
}
19081934
}
19091935

1936+
/// Specific direction for FETCH statement
1937+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1938+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1939+
pub enum FetchDirection {
1940+
Count { limit: Value },
1941+
Next,
1942+
Prior,
1943+
First,
1944+
Last,
1945+
Absolute { limit: Value },
1946+
Relative { limit: Value },
1947+
All,
1948+
// FORWARD
1949+
// FORWARD count
1950+
Forward { limit: Option<Value> },
1951+
ForwardAll,
1952+
// BACKWARD
1953+
// BACKWARD count
1954+
Backward { limit: Option<Value> },
1955+
BackwardAll,
1956+
}
1957+
1958+
impl fmt::Display for FetchDirection {
1959+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1960+
match self {
1961+
FetchDirection::Count { limit } => f.write_str(&limit.to_string())?,
1962+
FetchDirection::Next => f.write_str("NEXT")?,
1963+
FetchDirection::Prior => f.write_str("PRIOR")?,
1964+
FetchDirection::First => f.write_str("FIRST")?,
1965+
FetchDirection::Last => f.write_str("LAST")?,
1966+
FetchDirection::Absolute { limit } => {
1967+
f.write_str("ABSOLUTE ")?;
1968+
f.write_str(&limit.to_string())?;
1969+
}
1970+
FetchDirection::Relative { limit } => {
1971+
f.write_str("RELATIVE ")?;
1972+
f.write_str(&limit.to_string())?;
1973+
}
1974+
FetchDirection::All => f.write_str("ALL")?,
1975+
FetchDirection::Forward { limit } => {
1976+
f.write_str("FORWARD")?;
1977+
1978+
if let Some(l) = limit {
1979+
f.write_str(&format!(" {}", l))?;
1980+
}
1981+
}
1982+
FetchDirection::ForwardAll => f.write_str("FORWARD ALL")?,
1983+
FetchDirection::Backward { limit } => {
1984+
f.write_str("BACKWARD")?;
1985+
1986+
if let Some(l) = limit {
1987+
f.write_str(&format!(" {}", l))?;
1988+
}
1989+
}
1990+
FetchDirection::BackwardAll => f.write_str("BACKWARD ALL")?,
1991+
};
1992+
1993+
Ok(())
1994+
}
1995+
}
1996+
19101997
/// A privilege on a database object (table, sequence, etc.).
19111998
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19121999
#[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,
@@ -236,6 +238,7 @@ define_keywords!(
236238
FORCE_QUOTE,
237239
FOREIGN,
238240
FORMAT,
241+
FORWARD,
239242
FRAME_ROW,
240243
FREE,
241244
FREEZE,
@@ -383,6 +386,7 @@ define_keywords!(
383386
PREPARE,
384387
PRESERVE,
385388
PRIMARY,
389+
PRIOR,
386390
PRIVILEGES,
387391
PROCEDURE,
388392
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
@@ -168,6 +168,7 @@ impl<'a> Parser<'a> {
168168
Keyword::DROP => Ok(self.parse_drop()?),
169169
Keyword::DISCARD => Ok(self.parse_discard()?),
170170
Keyword::DECLARE => Ok(self.parse_declare()?),
171+
Keyword::FETCH => Ok(self.parse_fetch_statement()?),
171172
Keyword::DELETE => Ok(self.parse_delete()?),
172173
Keyword::INSERT => Ok(self.parse_insert()?),
173174
Keyword::UPDATE => Ok(self.parse_update()?),
@@ -1780,6 +1781,67 @@ impl<'a> Parser<'a> {
17801781
})
17811782
}
17821783

1784+
// FETCH [ direction { FROM | IN } ] cursor INTO target;
1785+
pub fn parse_fetch_statement(&mut self) -> Result<Statement, ParserError> {
1786+
let direction = if self.parse_keyword(Keyword::NEXT) {
1787+
FetchDirection::Next
1788+
} else if self.parse_keyword(Keyword::PRIOR) {
1789+
FetchDirection::Prior
1790+
} else if self.parse_keyword(Keyword::FIRST) {
1791+
FetchDirection::First
1792+
} else if self.parse_keyword(Keyword::LAST) {
1793+
FetchDirection::Last
1794+
} else if self.parse_keyword(Keyword::ABSOLUTE) {
1795+
FetchDirection::Absolute {
1796+
limit: self.parse_number_value()?,
1797+
}
1798+
} else if self.parse_keyword(Keyword::RELATIVE) {
1799+
FetchDirection::Relative {
1800+
limit: self.parse_number_value()?,
1801+
}
1802+
} else if self.parse_keyword(Keyword::FORWARD) {
1803+
if self.parse_keyword(Keyword::ALL) {
1804+
FetchDirection::ForwardAll
1805+
} else {
1806+
FetchDirection::Forward {
1807+
// TODO: Support optional
1808+
limit: Some(self.parse_number_value()?),
1809+
}
1810+
}
1811+
} else if self.parse_keyword(Keyword::BACKWARD) {
1812+
if self.parse_keyword(Keyword::ALL) {
1813+
FetchDirection::BackwardAll
1814+
} else {
1815+
FetchDirection::Backward {
1816+
// TODO: Support optional
1817+
limit: Some(self.parse_number_value()?),
1818+
}
1819+
}
1820+
} else if self.parse_keyword(Keyword::ALL) {
1821+
FetchDirection::All
1822+
} else {
1823+
FetchDirection::Count {
1824+
limit: self.parse_number_value()?,
1825+
}
1826+
};
1827+
1828+
self.expect_one_of_keywords(&[Keyword::FROM, Keyword::IN])?;
1829+
1830+
let name = self.parse_object_name()?;
1831+
1832+
let into = if self.parse_keyword(Keyword::INTO) {
1833+
Some(self.parse_object_name()?)
1834+
} else {
1835+
None
1836+
};
1837+
1838+
Ok(Statement::Fetch {
1839+
name,
1840+
direction,
1841+
into,
1842+
})
1843+
}
1844+
17831845
/// DECLARE name [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ]
17841846
// CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
17851847
pub fn parse_declare(&mut self) -> Result<Statement, ParserError> {

tests/sqlparser_postgres.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,3 +1484,27 @@ fn parse_declare() {
14841484
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" NO SCROLL CURSOR FOR SELECT 1");
14851485
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY INSENSITIVE SCROLL CURSOR WITH HOLD FOR SELECT * FROM table_name LIMIT 2222");
14861486
}
1487+
1488+
#[test]
1489+
fn parse_fetch() {
1490+
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\"");
1491+
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1492+
pg_and_generic().verified_stmt("FETCH NEXT IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1493+
pg_and_generic().verified_stmt("FETCH PRIOR IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1494+
pg_and_generic().verified_stmt("FETCH FIRST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1495+
pg_and_generic().verified_stmt("FETCH LAST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1496+
pg_and_generic()
1497+
.verified_stmt("FETCH ABSOLUTE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1498+
pg_and_generic()
1499+
.verified_stmt("FETCH RELATIVE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1500+
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1501+
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1502+
pg_and_generic()
1503+
.verified_stmt("FETCH FORWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1504+
pg_and_generic()
1505+
.verified_stmt("FETCH FORWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1506+
pg_and_generic()
1507+
.verified_stmt("FETCH BACKWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1508+
pg_and_generic()
1509+
.verified_stmt("FETCH BACKWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1510+
}

0 commit comments

Comments
 (0)