mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-19 13:40:15 +00:00
add SELECT INTO statement variation (#447)
Signed-off-by: Maciej Obuchowski <obuchowski.maciej@gmail.com>
This commit is contained in:
parent
b68e9a3801
commit
d38c30e9ff
7 changed files with 74 additions and 2 deletions
|
@ -36,8 +36,8 @@ pub use self::ddl::{
|
||||||
pub use self::operator::{BinaryOperator, UnaryOperator};
|
pub use self::operator::{BinaryOperator, UnaryOperator};
|
||||||
pub use self::query::{
|
pub use self::query::{
|
||||||
Cte, Fetch, Join, JoinConstraint, JoinOperator, LateralView, LockType, Offset, OffsetRows,
|
Cte, Fetch, Join, JoinConstraint, JoinOperator, LateralView, LockType, Offset, OffsetRows,
|
||||||
OrderByExpr, Query, Select, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor,
|
OrderByExpr, Query, Select, SelectInto, SelectItem, SetExpr, SetOperator, TableAlias,
|
||||||
TableWithJoins, Top, Values, With,
|
TableFactor, TableWithJoins, Top, Values, With,
|
||||||
};
|
};
|
||||||
pub use self::value::{DateTimeField, TrimWhereField, Value};
|
pub use self::value::{DateTimeField, TrimWhereField, Value};
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,8 @@ pub struct Select {
|
||||||
pub top: Option<Top>,
|
pub top: Option<Top>,
|
||||||
/// projection expressions
|
/// projection expressions
|
||||||
pub projection: Vec<SelectItem>,
|
pub projection: Vec<SelectItem>,
|
||||||
|
/// INTO
|
||||||
|
pub into: Option<SelectInto>,
|
||||||
/// FROM
|
/// FROM
|
||||||
pub from: Vec<TableWithJoins>,
|
pub from: Vec<TableWithJoins>,
|
||||||
/// LATERAL VIEWs
|
/// LATERAL VIEWs
|
||||||
|
@ -161,6 +163,11 @@ impl fmt::Display for Select {
|
||||||
write!(f, " {}", top)?;
|
write!(f, " {}", top)?;
|
||||||
}
|
}
|
||||||
write!(f, " {}", display_comma_separated(&self.projection))?;
|
write!(f, " {}", display_comma_separated(&self.projection))?;
|
||||||
|
|
||||||
|
if let Some(ref into) = self.into {
|
||||||
|
write!(f, " {}", into)?;
|
||||||
|
}
|
||||||
|
|
||||||
if !self.from.is_empty() {
|
if !self.from.is_empty() {
|
||||||
write!(f, " FROM {}", display_comma_separated(&self.from))?;
|
write!(f, " FROM {}", display_comma_separated(&self.from))?;
|
||||||
}
|
}
|
||||||
|
@ -636,3 +643,20 @@ impl fmt::Display for Values {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct SelectInto {
|
||||||
|
pub temporary: bool,
|
||||||
|
pub unlogged: bool,
|
||||||
|
pub name: ObjectName,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SelectInto {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let temporary = if self.temporary { " TEMPORARY" } else { "" };
|
||||||
|
let unlogged = if self.unlogged { " UNLOGGED" } else { "" };
|
||||||
|
|
||||||
|
write!(f, "INTO{}{} {}", temporary, unlogged, self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -499,6 +499,7 @@ define_keywords!(
|
||||||
UNION,
|
UNION,
|
||||||
UNIQUE,
|
UNIQUE,
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
|
UNLOGGED,
|
||||||
UNNEST,
|
UNNEST,
|
||||||
UNSIGNED,
|
UNSIGNED,
|
||||||
UPDATE,
|
UPDATE,
|
||||||
|
@ -600,4 +601,5 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[
|
||||||
Keyword::DISTRIBUTE,
|
Keyword::DISTRIBUTE,
|
||||||
// Reserved only as a column alias in the `SELECT` clause
|
// Reserved only as a column alias in the `SELECT` clause
|
||||||
Keyword::FROM,
|
Keyword::FROM,
|
||||||
|
Keyword::INTO,
|
||||||
];
|
];
|
||||||
|
|
|
@ -2892,6 +2892,21 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
let projection = self.parse_comma_separated(Parser::parse_select_item)?;
|
let projection = self.parse_comma_separated(Parser::parse_select_item)?;
|
||||||
|
|
||||||
|
let into = if self.parse_keyword(Keyword::INTO) {
|
||||||
|
let temporary = self
|
||||||
|
.parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY])
|
||||||
|
.is_some();
|
||||||
|
let unlogged = self.parse_keyword(Keyword::UNLOGGED);
|
||||||
|
let name = self.parse_object_name()?;
|
||||||
|
Some(SelectInto {
|
||||||
|
temporary,
|
||||||
|
unlogged,
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
// Note that for keywords to be properly handled here, they need to be
|
// Note that for keywords to be properly handled here, they need to be
|
||||||
// added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`,
|
// added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`,
|
||||||
// otherwise they may be parsed as an alias as part of the `projection`
|
// otherwise they may be parsed as an alias as part of the `projection`
|
||||||
|
@ -2973,6 +2988,7 @@ impl<'a> Parser<'a> {
|
||||||
distinct,
|
distinct,
|
||||||
top,
|
top,
|
||||||
projection,
|
projection,
|
||||||
|
into,
|
||||||
from,
|
from,
|
||||||
lateral_views,
|
lateral_views,
|
||||||
selection,
|
selection,
|
||||||
|
|
|
@ -380,6 +380,32 @@ fn parse_select_all_distinct() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_select_into() {
|
||||||
|
let sql = "SELECT * INTO table0 FROM table1";
|
||||||
|
one_statement_parses_to(sql, "SELECT * INTO table0 FROM table1");
|
||||||
|
let select = verified_only_select(sql);
|
||||||
|
assert_eq!(
|
||||||
|
&SelectInto {
|
||||||
|
temporary: false,
|
||||||
|
unlogged: false,
|
||||||
|
name: ObjectName(vec![Ident::new("table0")])
|
||||||
|
},
|
||||||
|
only(&select.into)
|
||||||
|
);
|
||||||
|
|
||||||
|
let sql = "SELECT * INTO TEMPORARY UNLOGGED table0 FROM table1";
|
||||||
|
one_statement_parses_to(sql, "SELECT * INTO TEMPORARY UNLOGGED table0 FROM table1");
|
||||||
|
|
||||||
|
// Do not allow aliases here
|
||||||
|
let sql = "SELECT * INTO table0 asdf FROM table1";
|
||||||
|
let result = parse_sql_statements(sql);
|
||||||
|
assert_eq!(
|
||||||
|
ParserError::ParserError("Expected end of statement, found: asdf".to_string()),
|
||||||
|
result.unwrap_err()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_select_wildcard() {
|
fn parse_select_wildcard() {
|
||||||
let sql = "SELECT * FROM foo";
|
let sql = "SELECT * FROM foo";
|
||||||
|
@ -4235,6 +4261,7 @@ fn parse_merge() {
|
||||||
distinct: false,
|
distinct: false,
|
||||||
top: None,
|
top: None,
|
||||||
projection: vec![SelectItem::Wildcard],
|
projection: vec![SelectItem::Wildcard],
|
||||||
|
into: None,
|
||||||
from: vec![TableWithJoins {
|
from: vec![TableWithJoins {
|
||||||
relation: TableFactor::Table {
|
relation: TableFactor::Table {
|
||||||
name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]),
|
name: ObjectName(vec![Ident::new("s"), Ident::new("foo")]),
|
||||||
|
|
|
@ -305,6 +305,7 @@ fn parse_quote_identifiers_2() {
|
||||||
value: "quoted ` identifier".into(),
|
value: "quoted ` identifier".into(),
|
||||||
quote_style: Some('`'),
|
quote_style: Some('`'),
|
||||||
}))],
|
}))],
|
||||||
|
into: None,
|
||||||
from: vec![],
|
from: vec![],
|
||||||
lateral_views: vec![],
|
lateral_views: vec![],
|
||||||
selection: None,
|
selection: None,
|
||||||
|
@ -732,6 +733,7 @@ fn parse_substring_in_select() {
|
||||||
false
|
false
|
||||||
))))
|
))))
|
||||||
})],
|
})],
|
||||||
|
into: None,
|
||||||
from: vec![TableWithJoins {
|
from: vec![TableWithJoins {
|
||||||
relation: TableFactor::Table {
|
relation: TableFactor::Table {
|
||||||
name: ObjectName(vec![Ident {
|
name: ObjectName(vec![Ident {
|
||||||
|
|
|
@ -53,6 +53,7 @@ fn parse_map_access_expr() {
|
||||||
distinct: false,
|
distinct: false,
|
||||||
})],
|
})],
|
||||||
})],
|
})],
|
||||||
|
into: None,
|
||||||
from: vec![TableWithJoins {
|
from: vec![TableWithJoins {
|
||||||
relation: Table {
|
relation: Table {
|
||||||
name: ObjectName(vec![Ident::new("foos")]),
|
name: ObjectName(vec![Ident::new("foos")]),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue