Skip to content

Commit

Permalink
feat: Support FETCH (cursors)
Browse files Browse the repository at this point in the history
  • Loading branch information
ovr committed May 25, 2022
1 parent 736cbce commit 8004f15
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 0 deletions.
87 changes: 87 additions & 0 deletions src/ast/mod.rs
Expand Up @@ -926,6 +926,17 @@ pub enum Statement {
hold: Option<bool>,
query: Box<Query>,
},
/// FETCH - retrieve rows from a query using a cursor
///
/// Note: this is a PostgreSQL-specific statement,
/// but may also compatible with other SQL.
Fetch {
/// Cursor name
name: ObjectName,
direction: FetchDirection,
/// Optional, It's possible to fetch rows form cursor to the table
into: Option<ObjectName>,
},
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
///
/// Note: this is a PostgreSQL-specific statement,
Expand Down Expand Up @@ -1136,6 +1147,21 @@ impl fmt::Display for Statement {
write!(f, "{}", statement)
}
Statement::Query(s) => write!(f, "{}", s),
Statement::Fetch {
name,
direction,
into,
} => {
write!(f, "FETCH {} ", direction)?;

write!(f, "IN {}", name)?;

if let Some(into) = into {
write!(f, " INTO {}", into)?;
}

Ok(())
}
Statement::Declare {
name,
binary,
Expand Down Expand Up @@ -1907,6 +1933,67 @@ impl fmt::Display for Privileges {
}
}

/// Specific direction for FETCH statement
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum FetchDirection {
Count { limit: Value },
Next,
Prior,
First,
Last,
Absolute { limit: Value },
Relative { limit: Value },
All,
// FORWARD
// FORWARD count
Forward { limit: Option<Value> },
ForwardAll,
// BACKWARD
// BACKWARD count
Backward { limit: Option<Value> },
BackwardAll,
}

impl fmt::Display for FetchDirection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FetchDirection::Count { limit } => f.write_str(&limit.to_string())?,
FetchDirection::Next => f.write_str("NEXT")?,
FetchDirection::Prior => f.write_str("PRIOR")?,
FetchDirection::First => f.write_str("FIRST")?,
FetchDirection::Last => f.write_str("LAST")?,
FetchDirection::Absolute { limit } => {
f.write_str("ABSOLUTE ")?;
f.write_str(&limit.to_string())?;
}
FetchDirection::Relative { limit } => {
f.write_str("RELATIVE ")?;
f.write_str(&limit.to_string())?;
}
FetchDirection::All => f.write_str("ALL")?,
FetchDirection::Forward { limit } => {
f.write_str("FORWARD")?;

if let Some(l) = limit {
f.write_str(&format!(" {}", l))?;
}
}
FetchDirection::ForwardAll => f.write_str("FORWARD ALL")?,
FetchDirection::Backward { limit } => {
f.write_str("BACKWARD")?;

if let Some(l) = limit {
f.write_str(&format!(" {}", l))?;
}
}
FetchDirection::BackwardAll => f.write_str("BACKWARD ALL")?,
};

Ok(())
}
}

/// A privilege on a database object (table, sequence, etc.).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand Down
5 changes: 5 additions & 0 deletions src/keywords.rs
Expand Up @@ -67,6 +67,7 @@ macro_rules! define_keywords {
define_keywords!(
ABORT,
ABS,
ABSOLUTE,
ACTION,
ADD,
ALL,
Expand All @@ -92,6 +93,7 @@ define_keywords!(
AUTO_INCREMENT,
AVG,
AVRO,
BACKWARD,
BEGIN,
BEGIN_FRAME,
BEGIN_PARTITION,
Expand Down Expand Up @@ -236,6 +238,7 @@ define_keywords!(
FORCE_QUOTE,
FOREIGN,
FORMAT,
FORWARD,
FRAME_ROW,
FREE,
FREEZE,
Expand Down Expand Up @@ -383,6 +386,7 @@ define_keywords!(
PREPARE,
PRESERVE,
PRIMARY,
PRIOR,
PRIVILEGES,
PROCEDURE,
PROGRAM,
Expand Down Expand Up @@ -411,6 +415,7 @@ define_keywords!(
REGR_SXX,
REGR_SXY,
REGR_SYY,
RELATIVE,
RELEASE,
RENAME,
REPAIR,
Expand Down
62 changes: 62 additions & 0 deletions src/parser.rs
Expand Up @@ -168,6 +168,7 @@ impl<'a> Parser<'a> {
Keyword::DROP => Ok(self.parse_drop()?),
Keyword::DISCARD => Ok(self.parse_discard()?),
Keyword::DECLARE => Ok(self.parse_declare()?),
Keyword::FETCH => Ok(self.parse_fetch_statement()?),
Keyword::DELETE => Ok(self.parse_delete()?),
Keyword::INSERT => Ok(self.parse_insert()?),
Keyword::UPDATE => Ok(self.parse_update()?),
Expand Down Expand Up @@ -1780,6 +1781,67 @@ impl<'a> Parser<'a> {
})
}

// FETCH [ direction { FROM | IN } ] cursor INTO target;
pub fn parse_fetch_statement(&mut self) -> Result<Statement, ParserError> {
let direction = if self.parse_keyword(Keyword::NEXT) {
FetchDirection::Next
} else if self.parse_keyword(Keyword::PRIOR) {
FetchDirection::Prior
} else if self.parse_keyword(Keyword::FIRST) {
FetchDirection::First
} else if self.parse_keyword(Keyword::LAST) {
FetchDirection::Last
} else if self.parse_keyword(Keyword::ABSOLUTE) {
FetchDirection::Absolute {
limit: self.parse_number_value()?,
}
} else if self.parse_keyword(Keyword::RELATIVE) {
FetchDirection::Relative {
limit: self.parse_number_value()?,
}
} else if self.parse_keyword(Keyword::FORWARD) {
if self.parse_keyword(Keyword::ALL) {
FetchDirection::ForwardAll
} else {
FetchDirection::Forward {
// TODO: Support optional
limit: Some(self.parse_number_value()?),
}
}
} else if self.parse_keyword(Keyword::BACKWARD) {
if self.parse_keyword(Keyword::ALL) {
FetchDirection::BackwardAll
} else {
FetchDirection::Backward {
// TODO: Support optional
limit: Some(self.parse_number_value()?),
}
}
} else if self.parse_keyword(Keyword::ALL) {
FetchDirection::All
} else {
FetchDirection::Count {
limit: self.parse_number_value()?,
}
};

self.expect_one_of_keywords(&[Keyword::FROM, Keyword::IN])?;

let name = self.parse_object_name()?;

let into = if self.parse_keyword(Keyword::INTO) {
Some(self.parse_object_name()?)
} else {
None
};

Ok(Statement::Fetch {
name,
direction,
into,
})
}

/// DECLARE name [ BINARY ] [ ASENSITIVE | INSENSITIVE ] [ [ NO ] SCROLL ]
// CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
pub fn parse_declare(&mut self) -> Result<Statement, ParserError> {
Expand Down
24 changes: 24 additions & 0 deletions tests/sqlparser_postgres.rs
Expand Up @@ -1484,3 +1484,27 @@ fn parse_declare() {
.verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" NO SCROLL CURSOR FOR SELECT 1");
pg_and_generic().verified_stmt("DECLARE \"SQL_CUR0x7fa44801bc00\" BINARY INSENSITIVE SCROLL CURSOR WITH HOLD FOR SELECT * FROM table_name LIMIT 2222");
}

#[test]
fn parse_fetch() {
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\"");
pg_and_generic().verified_stmt("FETCH 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic().verified_stmt("FETCH NEXT IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic().verified_stmt("FETCH PRIOR IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic().verified_stmt("FETCH FIRST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic().verified_stmt("FETCH LAST IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic()
.verified_stmt("FETCH ABSOLUTE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic()
.verified_stmt("FETCH RELATIVE 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic().verified_stmt("FETCH ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic()
.verified_stmt("FETCH FORWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic()
.verified_stmt("FETCH FORWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic()
.verified_stmt("FETCH BACKWARD 2048 IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
pg_and_generic()
.verified_stmt("FETCH BACKWARD ALL IN \"SQL_CUR0x7fa44801bc00\" INTO \"new_table\"");
}

0 comments on commit 8004f15

Please sign in to comment.