Skip to content

Commit d29cec9

Browse files
ovrmcheshkov
authored andcommitted
feat: Support FETCH (cursors)
Can drop this after rebase on commit 66a3082 "feat: Support FETCH (cursors) (apache#510)", first released in 0.18.0
1 parent d576510 commit d29cec9

File tree

4 files changed

+180
-0
lines changed

4 files changed

+180
-0
lines changed

src/ast/mod.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,17 @@ pub enum Statement {
911911
/// deleted along with the dropped table
912912
purge: bool,
913913
},
914+
/// FETCH - retrieve rows from a query using a cursor
915+
///
916+
/// Note: this is a PostgreSQL-specific statement,
917+
/// but may also compatible with other SQL.
918+
Fetch {
919+
/// Cursor name
920+
name: Ident,
921+
direction: FetchDirection,
922+
/// Optional, It's possible to fetch rows form cursor to the table
923+
into: Option<ObjectName>,
924+
},
914925
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
915926
///
916927
/// Note: this is a PostgreSQL-specific statement,
@@ -1121,6 +1132,21 @@ impl fmt::Display for Statement {
11211132
write!(f, "{}", statement)
11221133
}
11231134
Statement::Query(s) => write!(f, "{}", s),
1135+
Statement::Fetch {
1136+
name,
1137+
direction,
1138+
into,
1139+
} => {
1140+
write!(f, "FETCH {} ", direction)?;
1141+
1142+
write!(f, "IN {}", name)?;
1143+
1144+
if let Some(into) = into {
1145+
write!(f, " INTO {}", into)?;
1146+
}
1147+
1148+
Ok(())
1149+
}
11241150
Statement::Directory {
11251151
overwrite,
11261152
local,
@@ -1850,6 +1876,69 @@ impl fmt::Display for Privileges {
18501876
}
18511877
}
18521878

1879+
/// Specific direction for FETCH statement
1880+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1881+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1882+
pub enum FetchDirection {
1883+
Count { limit: Value },
1884+
Next,
1885+
Prior,
1886+
First,
1887+
Last,
1888+
Absolute { limit: Value },
1889+
Relative { limit: Value },
1890+
All,
1891+
// FORWARD
1892+
// FORWARD count
1893+
Forward { limit: Option<Value> },
1894+
ForwardAll,
1895+
// BACKWARD
1896+
// BACKWARD count
1897+
Backward { limit: Option<Value> },
1898+
BackwardAll,
1899+
}
1900+
1901+
impl fmt::Display for FetchDirection {
1902+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1903+
match self {
1904+
FetchDirection::Count { limit } => f.write_str(&limit.to_string())?,
1905+
FetchDirection::Next => f.write_str("NEXT")?,
1906+
FetchDirection::Prior => f.write_str("PRIOR")?,
1907+
FetchDirection::First => f.write_str("FIRST")?,
1908+
FetchDirection::Last => f.write_str("LAST")?,
1909+
FetchDirection::Absolute { limit } => {
1910+
f.write_str("ABSOLUTE ")?;
1911+
f.write_str(&limit.to_string())?;
1912+
}
1913+
FetchDirection::Relative { limit } => {
1914+
f.write_str("RELATIVE ")?;
1915+
f.write_str(&limit.to_string())?;
1916+
}
1917+
FetchDirection::All => f.write_str("ALL")?,
1918+
FetchDirection::Forward { limit } => {
1919+
f.write_str("FORWARD")?;
1920+
1921+
if let Some(l) = limit {
1922+
f.write_str(" ")?;
1923+
f.write_str(&l.to_string())?;
1924+
}
1925+
}
1926+
FetchDirection::ForwardAll => f.write_str("FORWARD ALL")?,
1927+
FetchDirection::Backward { limit } => {
1928+
f.write_str("BACKWARD")?;
1929+
1930+
if let Some(l) = limit {
1931+
f.write_str(" ")?;
1932+
f.write_str(&l.to_string())?;
1933+
}
1934+
}
1935+
FetchDirection::BackwardAll => f.write_str("BACKWARD ALL")?,
1936+
};
1937+
1938+
Ok(())
1939+
}
1940+
}
1941+
18531942
/// A privilege on a database object (table, sequence, etc.).
18541943
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18551944
#[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()?),
@@ -1793,6 +1794,67 @@ impl<'a> Parser<'a> {
17931794
})
17941795
}
17951796

1797+
// FETCH [ direction { FROM | IN } ] cursor INTO target;
1798+
pub fn parse_fetch_statement(&mut self) -> Result<Statement, ParserError> {
1799+
let direction = if self.parse_keyword(Keyword::NEXT) {
1800+
FetchDirection::Next
1801+
} else if self.parse_keyword(Keyword::PRIOR) {
1802+
FetchDirection::Prior
1803+
} else if self.parse_keyword(Keyword::FIRST) {
1804+
FetchDirection::First
1805+
} else if self.parse_keyword(Keyword::LAST) {
1806+
FetchDirection::Last
1807+
} else if self.parse_keyword(Keyword::ABSOLUTE) {
1808+
FetchDirection::Absolute {
1809+
limit: self.parse_number_value()?,
1810+
}
1811+
} else if self.parse_keyword(Keyword::RELATIVE) {
1812+
FetchDirection::Relative {
1813+
limit: self.parse_number_value()?,
1814+
}
1815+
} else if self.parse_keyword(Keyword::FORWARD) {
1816+
if self.parse_keyword(Keyword::ALL) {
1817+
FetchDirection::ForwardAll
1818+
} else {
1819+
FetchDirection::Forward {
1820+
// TODO: Support optional
1821+
limit: Some(self.parse_number_value()?),
1822+
}
1823+
}
1824+
} else if self.parse_keyword(Keyword::BACKWARD) {
1825+
if self.parse_keyword(Keyword::ALL) {
1826+
FetchDirection::BackwardAll
1827+
} else {
1828+
FetchDirection::Backward {
1829+
// TODO: Support optional
1830+
limit: Some(self.parse_number_value()?),
1831+
}
1832+
}
1833+
} else if self.parse_keyword(Keyword::ALL) {
1834+
FetchDirection::All
1835+
} else {
1836+
FetchDirection::Count {
1837+
limit: self.parse_number_value()?,
1838+
}
1839+
};
1840+
1841+
self.expect_one_of_keywords(&[Keyword::FROM, Keyword::IN])?;
1842+
1843+
let name = self.parse_identifier()?;
1844+
1845+
let into = if self.parse_keyword(Keyword::INTO) {
1846+
Some(self.parse_object_name()?)
1847+
} else {
1848+
None
1849+
};
1850+
1851+
Ok(Statement::Fetch {
1852+
name,
1853+
direction,
1854+
into,
1855+
})
1856+
}
1857+
17961858
pub fn parse_discard(&mut self) -> Result<Statement, ParserError> {
17971859
let object_type = if self.parse_keyword(Keyword::ALL) {
17981860
DiscardObject::ALL

tests/sqlparser_postgres.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,3 +1505,27 @@ fn parse_escaped_literal_string() {
15051505
"sql parser error: Unterminated encoded string literal at Line: 1, Column 8"
15061506
);
15071507
}
1508+
1509+
#[test]
1510+
fn parse_fetch() {
1511+
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\"");
1512+
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1513+
pg_and_generic().verified_stmt("FETCH NEXT IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1514+
pg_and_generic().verified_stmt("FETCH PRIOR IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1515+
pg_and_generic().verified_stmt("FETCH FIRST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1516+
pg_and_generic().verified_stmt("FETCH LAST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1517+
pg_and_generic()
1518+
.verified_stmt("FETCH ABSOLUTE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1519+
pg_and_generic()
1520+
.verified_stmt("FETCH RELATIVE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1521+
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1522+
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1523+
pg_and_generic()
1524+
.verified_stmt("FETCH FORWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1525+
pg_and_generic()
1526+
.verified_stmt("FETCH FORWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1527+
pg_and_generic()
1528+
.verified_stmt("FETCH BACKWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1529+
pg_and_generic()
1530+
.verified_stmt("FETCH BACKWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
1531+
}

0 commit comments

Comments
 (0)