Skip to content

Commit 4f4fb00

Browse files
committed
feat: Support FETCH (cursors)
1 parent 4070f3e commit 4f4fb00

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
@@ -903,6 +903,17 @@ pub enum Statement {
903903
/// deleted along with the dropped table
904904
purge: bool,
905905
},
906+
/// FETCH - retrieve rows from a query using a cursor
907+
///
908+
/// Note: this is a PostgreSQL-specific statement,
909+
/// but may also compatible with other SQL.
910+
Fetch {
911+
/// Cursor name
912+
name: ObjectName,
913+
direction: FetchDirection,
914+
/// Optional, It's possible to fetch rows form cursor to the table
915+
into: Option<ObjectName>,
916+
},
906917
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
907918
///
908919
/// Note: this is a PostgreSQL-specific statement,
@@ -1113,6 +1124,21 @@ impl fmt::Display for Statement {
11131124
write!(f, "{}", statement)
11141125
}
11151126
Statement::Query(s) => write!(f, "{}", s),
1127+
Statement::Fetch {
1128+
name,
1129+
direction,
1130+
into,
1131+
} => {
1132+
write!(f, "FETCH {} ", direction)?;
1133+
1134+
write!(f, "IN {}", name)?;
1135+
1136+
if let Some(into) = into {
1137+
write!(f, " INTO {}", into)?;
1138+
}
1139+
1140+
Ok(())
1141+
}
11161142
Statement::Directory {
11171143
overwrite,
11181144
local,
@@ -1842,6 +1868,67 @@ impl fmt::Display for Privileges {
18421868
}
18431869
}
18441870

1871+
/// Specific direction for FETCH statement
1872+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1873+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1874+
pub enum FetchDirection {
1875+
Count { limit: Value },
1876+
Next,
1877+
Prior,
1878+
First,
1879+
Last,
1880+
Absolute { limit: Value },
1881+
Relative { limit: Value },
1882+
All,
1883+
// FORWARD
1884+
// FORWARD count
1885+
Forward { limit: Option<Value> },
1886+
ForwardAll,
1887+
// BACKWARD
1888+
// BACKWARD count
1889+
Backward { limit: Option<Value> },
1890+
BackwardAll,
1891+
}
1892+
1893+
impl fmt::Display for FetchDirection {
1894+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1895+
match self {
1896+
FetchDirection::Count { limit } => f.write_str(&limit.to_string())?,
1897+
FetchDirection::Next => f.write_str("NEXT")?,
1898+
FetchDirection::Prior => f.write_str("PRIOR")?,
1899+
FetchDirection::First => f.write_str("FIRST")?,
1900+
FetchDirection::Last => f.write_str("LAST")?,
1901+
FetchDirection::Absolute { limit } => {
1902+
f.write_str("ABSOLUTE ")?;
1903+
f.write_str(&limit.to_string())?;
1904+
}
1905+
FetchDirection::Relative { limit } => {
1906+
f.write_str("RELATIVE ")?;
1907+
f.write_str(&limit.to_string())?;
1908+
}
1909+
FetchDirection::All => f.write_str("ALL")?,
1910+
FetchDirection::Forward { limit } => {
1911+
f.write_str("FORWARD")?;
1912+
1913+
if let Some(l) = limit {
1914+
f.write_str(&format!(" {}", l))?;
1915+
}
1916+
}
1917+
FetchDirection::ForwardAll => f.write_str("FORWARD ALL")?,
1918+
FetchDirection::Backward { limit } => {
1919+
f.write_str("BACKWARD")?;
1920+
1921+
if let Some(l) = limit {
1922+
f.write_str(&format!(" {}", l))?;
1923+
}
1924+
}
1925+
FetchDirection::BackwardAll => f.write_str("BACKWARD ALL")?,
1926+
};
1927+
1928+
Ok(())
1929+
}
1930+
}
1931+
18451932
/// A privilege on a database object (table, sequence, etc.).
18461933
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18471934
#[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
@@ -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::DELETE => Ok(self.parse_delete()?),
171172
Keyword::INSERT => Ok(self.parse_insert()?),
172173
Keyword::UPDATE => Ok(self.parse_update()?),
@@ -1779,6 +1780,67 @@ impl<'a> Parser<'a> {
17791780
})
17801781
}
17811782

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

tests/sqlparser_postgres.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,3 +1467,27 @@ fn pg_and_generic() -> TestedDialects {
14671467
dialects: vec![Box::new(PostgreSqlDialect {}), Box::new(GenericDialect {})],
14681468
}
14691469
}
1470+
1471+
#[test]
1472+
fn parse_fetch() {
1473+
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\"");
1474+
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1475+
pg_and_generic().verified_stmt("FETCH NEXT IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1476+
pg_and_generic().verified_stmt("FETCH PRIOR IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1477+
pg_and_generic().verified_stmt("FETCH FIRST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1478+
pg_and_generic().verified_stmt("FETCH LAST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1479+
pg_and_generic()
1480+
.verified_stmt("FETCH ABSOLUTE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1481+
pg_and_generic()
1482+
.verified_stmt("FETCH RELATIVE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1483+
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1484+
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1485+
pg_and_generic()
1486+
.verified_stmt("FETCH FORWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1487+
pg_and_generic()
1488+
.verified_stmt("FETCH FORWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1489+
pg_and_generic()
1490+
.verified_stmt("FETCH BACKWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1491+
pg_and_generic()
1492+
.verified_stmt("FETCH BACKWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1493+
}

0 commit comments

Comments
 (0)