mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-21 06:30:14 +00:00
feat: Support FETCH (cursors) (#510)
This commit is contained in:
parent
d19d955d9b
commit
66a3082cb6
4 changed files with 180 additions and 0 deletions
|
@ -895,6 +895,17 @@ pub enum Statement {
|
||||||
/// deleted along with the dropped table
|
/// deleted along with the dropped table
|
||||||
purge: bool,
|
purge: bool,
|
||||||
},
|
},
|
||||||
|
/// 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: Ident,
|
||||||
|
direction: FetchDirection,
|
||||||
|
/// Optional, It's possible to fetch rows form cursor to the table
|
||||||
|
into: Option<ObjectName>,
|
||||||
|
},
|
||||||
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
|
/// DISCARD [ ALL | PLANS | SEQUENCES | TEMPORARY | TEMP ]
|
||||||
///
|
///
|
||||||
/// Note: this is a PostgreSQL-specific statement,
|
/// Note: this is a PostgreSQL-specific statement,
|
||||||
|
@ -1114,6 +1125,21 @@ impl fmt::Display for Statement {
|
||||||
write!(f, "{}", statement)
|
write!(f, "{}", statement)
|
||||||
}
|
}
|
||||||
Statement::Query(s) => write!(f, "{}", s),
|
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::Directory {
|
Statement::Directory {
|
||||||
overwrite,
|
overwrite,
|
||||||
local,
|
local,
|
||||||
|
@ -1859,6 +1885,69 @@ 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(" ")?;
|
||||||
|
f.write_str(&l.to_string())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FetchDirection::ForwardAll => f.write_str("FORWARD ALL")?,
|
||||||
|
FetchDirection::Backward { limit } => {
|
||||||
|
f.write_str("BACKWARD")?;
|
||||||
|
|
||||||
|
if let Some(l) = limit {
|
||||||
|
f.write_str(" ")?;
|
||||||
|
f.write_str(&l.to_string())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FetchDirection::BackwardAll => f.write_str("BACKWARD ALL")?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A privilege on a database object (table, sequence, etc.).
|
/// A privilege on a database object (table, sequence, etc.).
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
|
|
@ -67,6 +67,7 @@ macro_rules! define_keywords {
|
||||||
define_keywords!(
|
define_keywords!(
|
||||||
ABORT,
|
ABORT,
|
||||||
ABS,
|
ABS,
|
||||||
|
ABSOLUTE,
|
||||||
ACTION,
|
ACTION,
|
||||||
ADD,
|
ADD,
|
||||||
ALL,
|
ALL,
|
||||||
|
@ -93,6 +94,7 @@ define_keywords!(
|
||||||
AUTO_INCREMENT,
|
AUTO_INCREMENT,
|
||||||
AVG,
|
AVG,
|
||||||
AVRO,
|
AVRO,
|
||||||
|
BACKWARD,
|
||||||
BEGIN,
|
BEGIN,
|
||||||
BEGIN_FRAME,
|
BEGIN_FRAME,
|
||||||
BEGIN_PARTITION,
|
BEGIN_PARTITION,
|
||||||
|
@ -239,6 +241,7 @@ define_keywords!(
|
||||||
FORCE_QUOTE,
|
FORCE_QUOTE,
|
||||||
FOREIGN,
|
FOREIGN,
|
||||||
FORMAT,
|
FORMAT,
|
||||||
|
FORWARD,
|
||||||
FRAME_ROW,
|
FRAME_ROW,
|
||||||
FREE,
|
FREE,
|
||||||
FREEZE,
|
FREEZE,
|
||||||
|
@ -387,6 +390,7 @@ define_keywords!(
|
||||||
PREPARE,
|
PREPARE,
|
||||||
PRESERVE,
|
PRESERVE,
|
||||||
PRIMARY,
|
PRIMARY,
|
||||||
|
PRIOR,
|
||||||
PRIVILEGES,
|
PRIVILEGES,
|
||||||
PROCEDURE,
|
PROCEDURE,
|
||||||
PROGRAM,
|
PROGRAM,
|
||||||
|
@ -415,6 +419,7 @@ define_keywords!(
|
||||||
REGR_SXX,
|
REGR_SXX,
|
||||||
REGR_SXY,
|
REGR_SXY,
|
||||||
REGR_SYY,
|
REGR_SYY,
|
||||||
|
RELATIVE,
|
||||||
RELEASE,
|
RELEASE,
|
||||||
RENAME,
|
RENAME,
|
||||||
REPAIR,
|
REPAIR,
|
||||||
|
|
|
@ -167,6 +167,7 @@ impl<'a> Parser<'a> {
|
||||||
Keyword::CREATE => Ok(self.parse_create()?),
|
Keyword::CREATE => Ok(self.parse_create()?),
|
||||||
Keyword::DROP => Ok(self.parse_drop()?),
|
Keyword::DROP => Ok(self.parse_drop()?),
|
||||||
Keyword::DISCARD => Ok(self.parse_discard()?),
|
Keyword::DISCARD => Ok(self.parse_discard()?),
|
||||||
|
Keyword::FETCH => Ok(self.parse_fetch_statement()?),
|
||||||
Keyword::DELETE => Ok(self.parse_delete()?),
|
Keyword::DELETE => Ok(self.parse_delete()?),
|
||||||
Keyword::INSERT => Ok(self.parse_insert()?),
|
Keyword::INSERT => Ok(self.parse_insert()?),
|
||||||
Keyword::UPDATE => Ok(self.parse_update()?),
|
Keyword::UPDATE => Ok(self.parse_update()?),
|
||||||
|
@ -1824,6 +1825,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_identifier()?;
|
||||||
|
|
||||||
|
let into = if self.parse_keyword(Keyword::INTO) {
|
||||||
|
Some(self.parse_object_name()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Statement::Fetch {
|
||||||
|
name,
|
||||||
|
direction,
|
||||||
|
into,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_discard(&mut self) -> Result<Statement, ParserError> {
|
pub fn parse_discard(&mut self) -> Result<Statement, ParserError> {
|
||||||
let object_type = if self.parse_keyword(Keyword::ALL) {
|
let object_type = if self.parse_keyword(Keyword::ALL) {
|
||||||
DiscardObject::ALL
|
DiscardObject::ALL
|
||||||
|
|
|
@ -1513,3 +1513,27 @@ fn parse_escaped_literal_string() {
|
||||||
"sql parser error: Unterminated encoded string literal at Line: 1, Column 8"
|
"sql parser error: Unterminated encoded string literal at Line: 1, Column 8"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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\"");
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue